このブログはCloudFrontからLightsailで作成したオリジンを参照する構成になっているのですが、最近オリジンに直接リクエストが来ることが増えました。間違えてオリジンのURLを返している可能性もあったのでログを解析してたのですが、大方の予想通りセキュリティホールを探っている物ばかりでした。
めちゃめちゃ気持ち悪い(´・ω・`) オリジンへのリクエストはCloudFrontからしか来ないと割り切って、80/443番へ直接来るリクエストはすべて拒否することにしました。
ここではApacheの設定を掲載しますが、同様の手法でnginxなど他のWebサーバーでも利用できると思います。
原理
動作原理は非常にシンプルです。
CloudFrontからオリジンへリクエストを投げる際に、秘密の独自HTTPヘッダーを付与します。オリジン側ではこの独自HTTPヘッダーが付いていればCloudFrontからのリクエストと判断。もし付いていなければ403を返して拒否します。
CloudFrontでは「Origin Custom Headers」という設定項目で自由にヘッダーを付けることができるのでこれを利用します。
CloudFront側の設定
Origin Custom Headersには今回X-Cdn-Secret
という独自のHTTPヘッダーを追加し、値にはランダムな文字列を16桁指定しました。ヘッダー名も値も推測されにくければ何でもかまいません。これで設定を反映したらステータスがDeployedになるまで待ちます。
ちなみに独自ヘッダーの先頭にX-
を付けるやり方は10年ほど前までは頻繁に利用されていましたが、最近では推奨されないようです。
独自のヘッダーは、以前は X- 接頭辞を使用していましたが、この慣習は 2012 年 6 月に非推奨になりました。これは、 RFC 6648 で非標準のフィールドが標準になったときに発生した不便さのためです。
※MDN - HTTPヘッダーより
……ですが、私が勝手に命名したこのフィールドが標準仕様になることは考えにくいのでこのまま行きます。
オリジン側の設定
運用中のサーバの場合、いきなりオリジン側で拒否するのではなくCloudFrontから値が渡ってきているか検証してから拒否する設定を入れます。
Webサーバのログに出力
httpd.confを開きLogFormatを新たに追加します。ここではcombined2という名前で定義しました。安直w
$ sudo vi /etc/httpd/conf/httpd.conf
<IfModule log_config_module> # 中略 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Cdn-Secret}i\"" combined2
今回はVirtualHostで管理していたサーバでしたので、CustomLogに先ほど新規に追加したLogFormatを指定します。
$ sudo vi /etc/httpd/conf.d/vhost.conf
<VirtualHost *:80> ServerName origin.example.com CustomLog logs/origin.example.com-access_log combined2
あとはApacheを再起動などして設定を反映します。
$ sudo systemctl restart httpd
しばらくしてApacheのログを確認し、CloudFrontからトークンが渡ってきていれば次のステップへ進みます。
$ sudo grep 'BIIklPy5qQmgE7ol' /var/log/httpd/origin.example.com-access_log 64.252.110.127 - - [04/Nov/2020:18:17:21 +0900] "GET / HTTP/1.1" 200 113612 "-" "Amazon CloudFront" "BIIklPy5qQmgE7ol"
Webサーバで拒否設定
拒否設定は非常に簡単で、以下の例で言う10〜13行目がそれにあたります。
<VirtualHost *:80> ServerName origin.example.com DocumentRoot /web/public CustomLog logs/origin.example.com-access_log combined2 ErrorLog logs/origin.example.com-error_log <Directory /web/public> Require all granted AllowOverride All SetEnvIf X-Cdn-Secret "^BIIklPy5qQmgE7ol" CDNValid Order deny,allow Deny from all Allow from env=CDNValid </Directory> </VirtualHost>
あとは再起動などして設定を反映します。
$ sudo systemctl restart httpd
効果検証
半日〜1日程度経過したら、アクセスログからトークンが含まれていないログを抽出し、403で正常に拒否できているか確認してみます。
$ sudo grep -v 'BIIklPy5qQmgE7ol' /var/log/httpd/origin.example.com-access_log 159.203.128.47 - - [04/Nov/2020:14:05:28 +0900] "GET / HTTP/1.0" 403 4890 "-" "masscan/1.0 (https://github.com/robertdavidgraham/masscan)" "-" 114.222.156.160 - - [04/Nov/2020:15:03:42 +0900] "GET /phpmyadmin/ HTTP/1.1" 403 199 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, li
バッチリですね👍