全然たいした話ではないのですが、へーって思ったので記録しておきます。
ALB にて外部からの不正アクセスを塞いだ話になります。
はじめに注意
※追記3この記事は、知識不足な状態で始まり、知識不足なまま初出した未熟な内容であり、外部の助力によりそれが解決に向かう、という流れになっています。
調査環境がAWSだったために、タイトルがこうなっていますが、実際はALB+ACM単独の問題ではなく、SSL/TLS としての仕様の話になっている、
ということを念頭において、読んでいただければと思います。
※追記3ここまで
構成と問題点
手動で作成された ALB → EC2 環境があって、ワイルドカードなACM を使って 0.0.0.0:443 のみ開いており、EC2 は Global からのアクセスは遮断してありました。にも関わらず、不正系なHostヘッダでアクセスされた形跡があり、コイツどこから来てんねんってことで調べてみました。
原因調査
基本的なチェックでは、ちゃんと想定通りのアクセス成功と拒否がされたのですが、あるアクセス方法でEC2まで貫通することが判明しました。curl
まず、curl でのアクセスで、想定ドメインでのHTTPSアクセスが通り、直Globalアドレスでは通らないことを確認。これは通る。
1 2 3 4 5 |
$ curl -v https://test.example.com/healthcheck > GET /healthcheck HTTP/1.1 > Host: test.example.com < HTTP/1.1 200 OK |
直アドレスはSSLで失敗して終了。当然TargetGroupに転送されない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ curl -v https://1.2.3.400/healthcheck -H "Host: test.example.com" * About to connect() to 1.2.3.400 port 443 (#0) * Trying 1.2.3.400... * Connected to 1.2.3.400 (1.2.3.400) port 443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none * Server certificate: * subject: CN=*.example.com * start date: Jun 02 00:00:00 2021 GMT * expire date: Jul 01 23:59:59 2022 GMT * common name: *.example.com * issuer: CN=Amazon,OU=Server CA 1B,O=Amazon,C=US * NSS error -12276 (SSL_ERROR_BAD_CERT_DOMAIN) * Unable to communicate securely with peer: requested domain name does not match the server's certificate. * Closing connection 0 |
Chrome
Chromeで普通にURL欄に入力したら、下記2つのどちらも通りました。TargetGroup先にあるEC2で、WEBサーバーのログや tcpdump で確認すると、なんと直Globalアドレスでのアクセスも通ってくることを確認。
不正アクセス
ということは、curl とブラウザでのHTTPSリクエスト内容は、証明書関係の部分の処理が異なっているということで、その詳細を確認する気力はないけども、すなわちGlobalアドレスさえあれば、443番ポートへのHTTPS通信を通せるかもな方法があるということであり、AWSのアドレス範囲はバレてていくらでも不正アクセスがくるので、現象としては筋が通ったということになります。
対策
そもそも ALB Listener Rule が下記のようになっていたので、ちゃんとHostヘッダをチェックするよう変更すればいいというだけの話。
本番環境とかは Terraform で構成しているので、元々そういう形になっていますが、開発者が自由に使った場合、わざわざルールをこうするかというと微妙なところなので、気をつけておきたいところです。
あとは二重気味の対策になっちゃいますが、WEBサーバーのホスト設定で、記述したドメイン群ではないHostヘッダが来た場合、多くは一番上の設定を使ったりするのですが、そういうアクセスを受けてエラーを返す設定を入れておくのもアリです。
ただしその場合、TargetGroupからのヘルスチェックには任意のヘッダを付与できないので、ちと面倒なWEB設定を記述することになるかもって感じです。
疑問
自分、SSL証明書は昔から扱ってますが、その仕様詳細については素人なので普通に疑問が残りました。暗号化されたリクエストは、HeaderもBodyも暗号化されて、FQDNの一致する証明書をもって復号されると思っていました。昔よく Wireshark とかで見たし。
ですが、直IPアドレスでのアクセスでドメイン情報がないのに、ALB + ACM では Target に通常通りリクエストが飛んだので、ドメイン関係なく復号された?またはHTTPSなのに、そもそも暗号化されずにリクエストされ、それをALBが受け入れた?ということになります。
形だけの443番ポートってわけじゃなく、ACMでHTTPSとして受け付けているのに、ドメイン情報なしに後ろまでリクエストが貫通してしまうのって、正しい仕様なのか、落とし穴なのか。Listener Rule ちゃんとすれば、って話じゃないと思うんですけど、どうなんでしょうね。
助言
※追記1@fujiwara さんがかまってくれました。
サーバーが提示した証明書を検証した上で通信していいかを決めるのはクライアントの責務なので(curlでも-kをつけると検証エラーでも通信できます)、そういうものだと思います
— fujiwara (@fujiwara) April 1, 2022
まさか、クライアントが暗号化しないって選択・フローがあるとは思わないやん……?
この辺を軸にもう少し理解しておくとしましょうそうしましょう。
※追記その2
最終的には、十分に腑に落ちる理解をしましたが、追記1までだとだいぶ勘違いされる締まりになっていたので追記2しておきます。
証明書による認証と、鍵での暗号化/復号は別の話で、HTTPSサーバーはどちらもやりとりするけど、クライアントが認証を無視しても暗号化通信はできるぜってことですな。で、暗号化通信に実はドメインは不要だから直IPアドレスでも通れましたよ、って結果だったという感じか
— 外道父 | Noko (@GedowFather) April 1, 2022
感想戦
色々よくない記事だったので、振り返り。開き直るわけではないけども、実際、ITインフラの構築の実現に、SSLの細かい仕様の勉強って不要なわけで、しかし今回は運用相談で対応はできても理屈は理解してなかったわけで、どこまで知識を掘り下げておくべきか、の難しいところよな…
— 外道父 | Noko (@GedowFather) April 2, 2022
職種的に、今回のは理解してて当然な知識だったのかもだけど、ココを掘り下げておく必要性がなかったんだよなぁ……。
強いて言うなら、普段はブログを書いている段階で『疑問』が残ってたら『解決』してから書き直すようにしていましたが、今回はモロ『疑問』と書き残して終わりにしたところですかね。知らんことは悪くないけど、わからんままは良くないっていう初心に振り返る回でございました。
※追記その2終わり
イチャモンつけたいわけじゃないし、面倒なのでサポートに投げたりしませんが、なんか少しだけ裏切られたというか、自分の常識にヒビを入れられた気がしました:-)