
はじめに
特定の IP アドレスから過剰にリクエストが来る場合に AWS WAF で IP アドレスベースのレート制限を設定できる.しかし IP アドレスが分散されていると IP アドレスベースのレート制限ではブロックできず,閾値を下げると正常なリクエストまでブロックしてしまう可能性が高まってしまう.
そんなときに1つの選択肢になるのが「JA4 フィンガープリント (JA4 fingerprinting)」で TLS クライアントの特徴からクライアントを識別できる.ようするに IP アドレスが分散していたとしても,TLS クライアントの特徴が同じであれば同じ JA4 フィンガープリントになるという仕組み❗️そして AWS WAF は JA4 フィンガープリントを使ったブロックとレート制限をサポートしている👌
検証環境に AWS WAF をデプロイして試してみた \( 'ω')/
JA4 フィンガープリントとは
JA4 は TLS クライアントの特徴を1つの文字列にまとめたフィンガープリントで,GitHub に仕様がまとまっている.1つ前の JA3 というバージョンもあるけど,現時点では JA4 を使うことが推奨されている.
JA4 フィンガープリントは t13d1517h2_8daaf6152771_a87ad97598a9 のような文字列になっている.1文字目の t は TCP を表していて,2,3文字目の 13 は TLS のバージョンを表している.詳しくは Cloudflare Blog に載っている画像がわかりやすかった.

サクッと JA4 フィンガープリントを確認する場合は BrowserLeaks の TLS Client Test にアクセスすれば OK👌
もちろん正常なリクエストと異常なリクエストの JA4 フィンガープリントが同じ場合は間違ってブロックしてしまうことにも繋がるため,BLOCK の前に COUNT で様子を見るなど一般的な AWS WAF の運用上の工夫は IP アドレスベースと同じく必要になる.
検証環境にデプロイする
今回は Terraform で ALB + AWS WAF をデプロイして「JA4 フィンガープリントベースのブロック」と「JA4 フィンガープリントベースのレート制限」を試す.JA4 フィンガープリントは TLS クライアントの特徴からクライアントを識別するため ALB に HTTPS で接続できるようにしておく必要がある.
✔️ priority 1: block-specific-ja4
スマホ (Pixel 9) の Chrome アプリからアクセスすると取得できる JA4 フィンガープリント t13d1516h2_8daaf6152771_806a8c22fdea を完全にブロックする.
✔️ priority 2: rate-limit-by-ja4
その他の JA4 フィンガープリントは「60秒間に10回」を閾値にレート制限を設定する.
👾 waf.tf
ALB 関連のコードは割愛して,AWS WAF 関連に限定して載せておく.
resource "aws_wafv2_web_acl" "main" { name = "sandbox-waf-ja4" scope = "REGIONAL" default_action { allow {} } rule { name = "block-specific-ja4" priority = 1 action { block {} } statement { byte_match_statement { positional_constraint = "EXACTLY" search_string = "t13d1516h2_8daaf6152771_806a8c22fdea" field_to_match { ja4_fingerprint { fallback_behavior = "MATCH" } } text_transformation { priority = 0 type = "NONE" } } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "block-specific-ja4" sampled_requests_enabled = true } } rule { name = "rate-limit-by-ja4" priority = 2 action { block {} } statement { rate_based_statement { limit = 10 evaluation_window_sec = 60 aggregate_key_type = "CUSTOM_KEYS" custom_key { ja4_fingerprint { fallback_behavior = "MATCH" } } } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "rate-limit-by-ja4" sampled_requests_enabled = true } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "sandbox-waf-ja4" sampled_requests_enabled = true } }
またドキュメントに To use this match option, you must log your protection pack (web ACL) traffic. と書いてある通り,Amazon CloudWatch Logs へのログ出力(aws-waf-logs- プレフィックス)も忘れずに✋️
動作確認 1
まず,スマホでアクセスすると 403 Forbidden になった👌
期待通りに JA4 フィンガープリント t13d1516h2_8daaf6152771_806a8c22fdea をブロックできている.

動作確認 2
次に Chrome でアクセスを10回以上繰り返すと 403 Forbidden になった👌
期待通りに JA4 フィンガープリントで集約したレート制限を確認できた.

Amazon CloudWatch Logs 確認
AWS WAF ログを Amazon CloudWatch Logs Insights で確認すると JA4 フィンガープリントごとに ALLOW or BLOCK と terminatingRuleId を確認できた.
fields @timestamp, httpRequest.uri, ja4Fingerprint, action, terminatingRuleId | sort @timestamp asc

まとめ
AWS WAF で JA4 フィンガープリントを使ったブロックとレート制限を試してみた.今まで使ったことがなくて勉強になった❗️IP アドレスが分散しているような場合の選択肢として覚えておこう💪