Cloudflare - Bot JavaScript detections の利用法
Bot JavaScript detectins とは
JavaScript detections are another method that help Cloudflare identify bot requests.
Cloudflare の各 Bot プロダクト は受信した HTTP リクエストの特徴などから受動的に Bot らしさ(Bot Score)を算出し、 Bot からの保護に利用します。一方 JavaScript detections は Cloudflare からクライアントに検知用のスクリプトを渡しその結果を利用するという、能動的な働きかけを追加することで、検知を強化します。
These detections are implemented via a lightweight, invisible JavaScript code snippet that follows Cloudflare’s privacy standards ↗. JavaScript is injected only in response to requests for HTML pages or page views, excluding AJAX calls. API and mobile app traffic is unaffected. Additionally, code is not injected again until the current session expires. After page load, the script is deferred and utilizes a separate thread (where available) to ensure that performance impact is minimal.
The snippets of JavaScript will contain a source pointing to the challenge platform, with paths that start with /cdn-cgi/challenge-platform/.
プラン
Free プランでは Bot Fight Mode と同時に有効となり、有償プランでは有効・無効を設定できます。
プラン | Bots | JavaScript detections |
---|---|---|
Free | Bot Fight Mode | 常に有効 |
Pro, Business, Enterprise | Super Bot Fight Mode | 有効・無効の切り替え可能 |
Enterprise | Bot Management | 有効・無効の切り替え可能 |
Challenge Platform
JavaScript detections は WAF などでも利用される Challenge Platform の力を借ります。
ただ WAF アクションのチャレンジとは用途が異なり、JavaScript detections 自体がリクエストをブロックすることはありません。
プロダクト | 機能 | 用途 | cf_clearance cookie の発行 | Challenge Platform との通信 |
---|---|---|---|---|
Bot Management | JavaScript detections | ・Bot Score の補正 (検知 ➜ Score 1) ・検知結果を WAF、Workers などで利用(*) |
○ | 自ホスト/cdn-cgi/challenge-platform/... |
WAF | Managed Challenge | Challenge(Interactive、Non-Interactive など)に失敗したクライアントをブロック | ○ | 自ホスト/cdn-cgi/challenge-platform/... challenges.cloudflare.com/... |
WAF | JS Challnege | Challenge(Non-Interactive)に失敗したクライアントをブロック | ○ | 自ホスト/cdn-cgi/challenge-platform/... challenges.cloudflare.com... |
WAF | Interactive Challenge | Challenge(Interactive)に失敗したクライアントをブロック | ○ | 自ホスト/cdn-cgi/challenge-platform/... challenges.cloudflare.com/... |
(*) JavaScript detections の検知結果(Boolean)は
-
WAF Custom Rules などで使える Rule Field
の cf.bot_management.js_detection.passed -
Workers の variable
request.cf.botManagement.jsDetection.passed
に格納されます。
PASSED
の場合 true、FAILED
・MISSING
の場合 false となります。
JavaScript detections 検知の確認
Security > Events
で検知結果を確認することができます。
下記のような例になります。
-
JavaScript Verification
は Failure 、Bot source
JavaScript detections によりBot score
は 1 -
JavaScript Verification
は Passed 、Bot source
Machine learning によりBot score
は 99
Firewall イベントログでも確認可能
$ rclone cat r2:logs/firewall/...log.gz | jq '.Metadata.js_detection'
"PASSED"
"MISSING"
WAF Custom Rules での利用
WAF Custom Rules は下記の 2 つで JavaScript detections の結果を活用することができます。
- (Bot 検知で補正された)Bot Score 1
-
cf.bot_management.js_detection.passed
の Boolean
ここでは 2 を試しますが、前提条件は下記の通りです。
条件 | テスト環境 |
---|---|
You must have JavaScript detections enabled on your zone. | ゾーンに JavaScript detections を設定。 現時点ではゾーン全体で有効。 |
You must have updated your Content Security Policy headers for JavaScript detections. | 今回オリジンで CSP 利用中のため script-src に nonce を指定。JavaScript detections はその nonce でオリジナルコンテンツにインラインのスクリプト挿入。 同様に self も追加し /cdn-cgi/challenge-platform/... パスを許可。 |
You must not run this field on websocket endpoints. |
cf.bot_management.js_detection.passed フィールドを適用する URL は websocket のエンドポイントではない。 |
You must use the field in a custom rules expression that expects only browser traffic. |
cf.bot_management.js_detection.passed フィールドを適用する URL はブラウザーからのトラフィックを想定したエンドポイント。 |
The action should always be a managed challenge in case a legitimate user has not received the challenge for network or browser reasons. | アクションは Managed Challenge に設定。 |
The path specified in the rule builder should never be the first HTML page a user visits when browsing your site. | 親ページ / (HTML)を踏んでから対象の /jsd.php (コンテンツタイプ任意)にアクセスを想定。 |
保護対象のエンドポイントに対する Custom Rules 設定は下記のようになりました。
デモ
親ページへのアクセス
まず親ページ /
にリクエストを送ると、下記のようなシーケンスになります。
1️⃣ブラウザーは HTML ボディに埋め込まれた下記のようなスクリプトを見ることになります。
2️⃣ブラウザーが検証に成功し cf_clearance cookie を得ます。ドメインはゾーン自体となっています。
JavaScript detection での cf_clearance cookie の有効期間
JavaScript detections で発行された cf_clearance
cookie が有効なあいだは検知が発動されることはありません。
Dev Docs にある Additionally, code is not injected again until the current session expires.
の部分については、JavaScript detections の場合、cf_clearance
cookie 発行から 12 分程度すると再度 JavaScript detections が実施、cookie が更新される挙動が見れました。(cookie 自体の expire は 1 年)
保護されたエンドポイントへのリクエスト
この状態から WAF Custom Rules で保護された /jsd.php
にアクセスします。
3️⃣ 後続の /jsd.php
へのリクエストはJavaScript detections を通過した cf_clearance
cookie を伴ったリクエストになります。そのため cf.bot_management.js_detection.passed
が true となり、Managed Challenge および JavaScript detections を受けることなく、オリジンに到達します。
4️⃣ オリジンからのレスポンスが HTML であっても JavaScript は埋め込まれません。
直接のアクセス
上記の cf_clearance
cookie がなく、直接アクセスした場合を想定します。
Managed Challenge
前提条件に記載の The action should always be a managed challenge in case a legitimate user has not received the challenge for network or browser reasons.
に従っているので Managed Challenge が発動します。
Managed Challenge を突破すると WAF で cf_clearance
cookie が発行されました。
コンテンツが HTML の場合は Managed Challenge 突破の後 JavaScript detections が実施され、成功により JavaScript detections の cf_clearance
cookie で上書きされました。
下記のような Challenge Platform が 2 度利用されるシーケンスで、複雑になりました。
Block
試しにアクションを Block にし、HTML のブロックページを利用してみました。
興味深いことにブロックページの HTML に対して JavaScript detections が発動しました。
つまり、ブラウザーは下記のようなブロックページの表示の過程で JavaScript detections をパスし、親ページにアクセスした場合と同じ状況となりました。
読み直すと、JavaScript detections されることなく、応答が返ります。
この挙動はデフォルトブロックページ、 Custom Pages 、どちらも同じでした。
また Custom HTML も同様でした。
ただ、こちらを使うと、親ページにリダイレクトしつつ、JavaScript detections を実施することができたので、Managed Challenge よりユーザーの見た目には優しかったです。
Custom HTML 例
<!doctype html><html><head>
<meta charset="utf-8" />
<meta http-equiv="refresh" content="1; url='https://<parent page>/'"/>
</head>
<body></body></html>
このように自動的に親ページが表示されたため、エンドポイントのリンクをたどることができました。
オリジンサーバーへの JavaScript detections 結果の伝達
JavaScript detections の結果をオリジンサーバーに伝達できるかを確認しました。
Transform Rules の Managed Transforms に bot protection headers がありましたが、残念ながら執筆時点で JavaScript detections は含まれていないようです。
そのため Workers(今回は Snippets)でヘッダーに埋め込み伝達しました。
snippets
export default {
async fetch(request) {
const newRequest = new Request(request)
const jsDetectionValue = request.cf?.botManagement?.jsDetection?.passed ?? 'unknown'
newRequest.headers.set(
'X-Bot-JS-Detection-Passed',
jsDetectionValue.toString()
)
return fetch(newRequest)
}
}
オリジン側でも Bot Score や JA4 Fingerprint など他のシグナルと合わせ、セキュリティのロジックの実装に利用できそうです。
さいごに
Bot 検知や WAF のチャレンジあたりは、かいくぐる方法が常に研究され進化し、公開されているものも新旧含めあります。一方、防御プラットフォーム側も継続開発がされており、イタチごっこが続きます。
日進月歩のため、本記事はあくまで執筆時点(2025年1月)の観測情報ということで、メモしておきます。
Discussion