[update] Amazon CloudFrontでJA3 fingerprint headersをサポートしました!
はじめに
清水です。AWS re:Invent 2022がはじまり、アップデート盛りだくさんになっていますね!そんな中ですが、re:Invent 2022前に発表されていたアップデートもしっかりおさえておこうのコーナーです。はい。
本エントリでお届けするアップデートはこちら!AWSのCDNサービスであるAmazon CloudFrontでJA3 fingerprint headersをサポートしました。(2022/11/17付でAWS What's Newにポストされたアップデート情報となります。)
- Amazon CloudFront now supports JA3 fingerprint headers
- Amazon CloudFront が JA3 フィンガープリントヘッダーのサポートを開始
JA3 Fingerprint!?
お恥ずかしながら、筆者は本アップデートをチェックするまでJA3 Fingerprintというものを把握できていませんでした。TLS(SSL/TLS)クライアントとの接続時のClient Helloパケット内容、具体的にはそのTLSバージョンや暗号方式、拡張機能リストなどをもとにMD5ハッシュ値を算出してこれをJA3 Fingerprintと呼ぶとのことです。
- Open Sourcing JA3 - Salesforce Engineering Blog
- TLS Fingerprinting with JA3 and JA3S - Salesforce Engineering Blog
- JA3 Fingerprinting: Functionality, Pitfalls, and Future Outlook
- JA3 on guard against bots
クライアントアプリケーションが同一であれば、User-AgentヘッダやIPアドレスの情報などに関わらず同一(同一種)の値となるようで、これを用いることで既知のマルウェアや悪意のあるボットであるかどうかの判断が可能になります。例えばこちらにはTorクライアント、Trickbotマルウェア、そしてEmotetマルウェアそれぞれのJA3 Fingerprintが公開されています。またあらかじめ接続するクライアントアプリケーションのJA3 Fingerprintがわかっていれば、そのクライアントのみを接続許可するといった利用もできるようです。
今回のCloudFrontのアップデートでは、CloudFront側でこのJA3 Fingerprintを算出、それをCloudFront-Viewer-JA3-Finfgerprint
というヘッダとしてオリジンに転送可能となった、ということになります。またオリジンに転送してオリジン側の処理で使用するほか、CloudFront FunctionsやLambda@Edgeでも利用可能です。
CloudFrontでJA3 fingerprint headersを使ってみた
それでは実際にこのCloudFront-Viewer-JA3-Fingerprint
ヘッダについて、CloudFrontで設定を行い使ってみたいと思います。使い方はほかのCloudFront HTTPヘッダーの利用方法と同様です。Origin request policyにCloudFront-Viewer-JA3-Fingerprint
ヘッダを追加する必要がある点に注意しましょう。
オリジンサーバの準備
オリジンサーバとして内部でApache HTTP ServerとPHPを動作させたEC2インスタンスを準備しました。また以下のファイル(index.php
)を配置しておきます。デフォルトルート(/
)にアクセスすれば、アクセス時のリクエストヘッダ情報が参照できる状態としました。
<?php foreach (getallheaders() as $name => $value) { echo "<p>"; echo "$name: $value"; echo "</p>\n"; } ?>
Origin request policyの作成
続いてCloudFront側の設定です。まずはOrigin request policyを作成していきます。CloudFrontマネジメントコンソールのPoliciesのページの「Origin request」タブから、[Create origin request policy]ボタンで進みます。NameとDescriptionを適切に設定します。
Origin request settingsの項目ではHeadersで「All viewer headers and the following CloudFront headers」を選択しました。(All viewer headersは任意ですが、CloudFront headersを選択する必要があります。)Add headerでOrigin requestに含めるCloudFront headersとして、CloudFront-Viewer-JA3-Fingerprint
を選択します。
[Create]ボタンで作成します。
Distributionの作成
Origin request policyが作成できたら、このOrigin request policyを使用するDistributionを作成していきます。ポイントとなる「Cache key and origin requests」の項目のみまとめます。
「Cache key and origin requests」の項目で「Cache policy and origin request policy (recommended)」を選択します。Cache policyは今回は検証目的ということで「CachingDisabled」を選択しました。Origin request policyで先ほど作成したCloudFront-Viewer-JA3-Fingerprint
をCloudFront headersに含めたもの指定します。(ここでは「CloudFrontViewerJA3FingerprintHeader」。)
JA3 Fingerprintヘッダの値の確認
CloudFront Distributionが作成できたら、このドメイン名にアクセスしてみます。まずはGoogle Chromeでのアクセスです。CloudFront-Viewer-JA3-Fingerprintヘッダの値として、cd08e31494f9531f560d64c695473da9
が表示されました。
プロファイルを変更しても同じ値となりました。(Chromeのテーマカラーで別プロファイルと察してください。)
ただし、タイミングによっては別のJA3 Fingerprintの値となる場合もありました。0d69ff451640d67ee8b5122752834766
という値ですね。
以下にmacOSならびにLinuxでのJA3ハッシュ値がまとめられているのですが、同じGoogle Chromeブラウザ(クライアントアプリケーション)でも複数のハッシュ値を取りうるもの、と理解しています。(そして今回得られたJA3 Fingerprintがこのリスト中にありませんが、OSやブラウザバージョン等によっても変わる可能性があるものかと考えます。)
- GitHub - salesforce/ja3: JA3 is a standard for creating SSL client fingerprints in an easy to produce and shareable way.
- ja3/lists at master · salesforce/ja3 · GitHub
- https://raw.githubusercontent.com/salesforce/ja3/master/lists/osx-nix-ja3.csv
続いてSafariブラウザでも確認してみましょう。773906b0efdefa24a7f2b8eb6985bf37
という値となりました。Safariブラウザでは確認した限り、値が変わることはありませんでした。
curlコマンドでも確認してみましょう。375c6162a492dfbf2795909110ce8424
という値でした。
% curl https://d370xxxxxxxxxx.cloudfront.net <p>Host: d370xxxxxxxxxx.cloudfront.net</p> <p>User-Agent: curl/7.79.1</p> <p>X-Amz-Cf-Id: L4N2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXg==</p> <p>Connection: Keep-Alive</p> <p>Via: 2.0 0b3axxxxxxxxxxxxxxxxxxxxxxxx6fd6.cloudfront.net (CloudFront)</p> <p>X-Forwarded-For: 2axx:xxxx:xxxx:xx::xx:x30</p> <p>Accept: */*</p> <p>CloudFront-Viewer-JA3-Fingerprint: 375c6162a492dfbf2795909110ce8424</p>
興味深い点として、例えばGoogle Chromeブラウザでデベロッパーツールを使ってリクエストから「Copy all as cURL」を行います。これで得られたcurlコマンドをターミナルで実行すると、User-Agentヘッダなど可能な限りGoogle Chromeブラウザでのリクエストと同じものがcurlコマンドでリクエストできるわけですが、JA3 Fingerprintの値は異なる結果(curlコマンドでのリクエストの場合はあくまでcurlコマンドでのJA3 Fingerprint値)となりました。
% curl 'https://d370xxxxxxxxxx.cloudfront.net/' \ -H 'authority: d370xxxxxxxxxx.cloudfront.net' \ -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \ -H 'accept-language: ja,en-US;q=0.9,en;q=0.8' \ -H 'sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'sec-ch-ua-platform: "macOS"' \ -H 'sec-fetch-dest: document' \ -H 'sec-fetch-mode: navigate' \ -H 'sec-fetch-site: none' \ -H 'sec-fetch-user: ?1' \ -H 'upgrade-insecure-requests: 1' \ -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' \ --compressed <p>Host: d370xxxxxxxxxx.cloudfront.net</p> <p>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36</p> <p>X-Amz-Cf-Id: Qmdwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyw==</p> <p>Connection: Keep-Alive</p> <p>Via: 2.0 d1faxxxxxxxxxxxxxxxxxxxxxxxx31ba.cloudfront.net (CloudFront)</p> <p>X-Forwarded-For: 2axx:xxxx:xxxx:xx::xx:x30</p> <p>Accept-Language: ja,en-US;q=0.9,en;q=0.8</p> <p>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9</p> <p>Accept-Encoding: deflate, gzip</p> <p>authority: d370xxxxxxxxxx.cloudfront.net</p> <p>sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"</p> <p>sec-ch-ua-mobile: ?0</p> <p>sec-ch-ua-platform: "macOS"</p> <p>sec-fetch-dest: document</p> <p>sec-fetch-mode: navigate</p> <p>sec-fetch-site: none</p> <p>sec-fetch-user: ?1</p> <p>upgrade-insecure-requests: 1</p> <p>CloudFront-Viewer-JA3-Fingerprint: 375c6162a492dfbf2795909110ce8424</p>
TLSを用いないHTTPリクエストではCloudFront-Viewer-JA3-Fingerprintヘッダは転送されない
CloudFront DistributionにCloudFront-Viewer-JA3-Fingerprint
ヘッダをリクエストに含めるよう設定したOrigin request policyを設定し、実際にオリジン側で転送されたCloudFront-Viewer-JA3-Fingerprint
ヘッダを確認してみました。ところで、JA3 FingerprintについてはTLSクライアントとの接続時のパケット内容をハッシュ値としたもの、ということでした。それではTLS(SSL/TLS)を用いない、HTTPSではないHTTPでの通信の場合、CloudFront-Viewer-JA3-Fingerprint
ヘッダはどうなるでしょうか。答えはCloudFrontからヘッダとして転送されない、となります。(Developer Guideにも、この旨記載がありますね。)
% curl http://d370xxxxxxxxxx.cloudfront.net <p>Host: d370xxxxxxxxxx.cloudfront.net</p> <p>User-Agent: curl/7.79.1</p> <p>X-Amz-Cf-Id: 6p02xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxfg==</p> <p>Connection: Keep-Alive</p> <p>Via: 1.1 45e3xxxxxxxxxxxxxxxxxxxxxxxx8d0a.cloudfront.net (CloudFront)</p> <p>X-Forwarded-For: 2axx:xxxx:xxxx:xx::xx:x30</p> <p>Accept: */*</p>
まとめ
Amazon CloudFrontで新たにサポートしたJA3 Fingerprintヘッダについて、実際にCloudFrontに設定しつつ、JA3 Fingerprintそのものも含めて確認してみました。今回は実際にマルウェアやボットのような悪意のあるリクエストがどのようなJA3 Fingerprintとなるか、といった確認はできませんでしたが、公開されている情報などをもとにしてこれらのJA3 Fingerprintハッシュ値を持つリクエストをブロックする、といったことが可能となります。本来想定している正常なリクエストと、異常な(悪意を持っていると思われる)リクエスト、それぞれの傾向を分析すると行った場合にも使用できそうですね。使用目的をきちんと理解し、有効に活用していきたいと思いました。