- ChatGPTでメッセージ送信時に Cloudflare Turnstileプログラム が実行され、ブラウザーフィンガープリントだけでなく Reactアプリケーションの状態 まで検査する
- 復号されたプログラムは 55個の属性 を収集し、ブラウザー・ネットワーク・アプリケーションの3層に分かれた検証手順を実行する
- Reactレンダリングが完了した実際のSPA環境でのみ通過可能で、ヘッドレスブラウザーや単純なボットのリクエストは失敗する
- 収集されたフィンガープリントは暗号化されて
OpenAI-Sentinel-Turnstile-Token に変換され、ここに Signal Orchestrator と Proof of Work モジュールが追加で動作する
- 復号キーを知っているのはCloudflareサーバーだけであり、プライバシー境界が技術ではなくポリシーによって決まる構造である
ChatGPTのメッセージ送信時におけるCloudflare Turnstileの動作構造分析
- ChatGPTのすべてのメッセージ送信時に ブラウザー内で Cloudflare Turnstileプログラム が自動実行される
- ネットワークトラフィックから377個のTurnstileプログラムを復号した結果、一般的なブラウザーフィンガープリント収集を超えて Reactアプリケーションの状態まで検査 していた
- 単純なブラウザーフィンガープリント偽装ボットは通過できず、ChatGPTのSPA(単一ページアプリケーション) を完全にレンダリングして初めて検証を通過できる
暗号化構造と復号過程
- Turnstileバイトコードはサーバー応答の
turnstile.dx フィールドで渡され、リクエストごとに28,000文字長のbase64文字列 として暗号化されている
- 外側の暗号化層は
p トークンとの XOR 演算で復号可能で、両者は同一のHTTPリクエスト内でやり取りされる
- 復号結果は 89個のVM命令 で構成されたJSON形式のバイトコードである
- 内部には 19KBサイズの追加暗号化ブロブ(blob) が存在し、このブロブは別の XOR キーで暗号化されている
- キーはバイトコード内の floatリテラル値(例: 97.35) として含まれており、サーバーが生成してブラウザーへ送信する
- 50回のリクエストすべてで、同じ方式により有効なJSONへの復号を確認した
- 全体の復号手順は次の5段階で構成される
- リクエストから
p トークンを読む
- 応答の
turnstile.dx を読む
XOR(base64decode(dx), p) → 外側のバイトコードを生成
- 19KBブロブの後ろにある5引数命令から最後の引数をキーとして抽出
XOR(base64decode(blob), str(key)) → 内部プログラムを復号(417〜580命令)
復号されたプログラムの検査項目
- 内部プログラムは 28個の命令語(opcode) を持つカスタムVMとして実行され、リクエストごとに浮動小数点レジスタのアドレスがランダムに変更される
- 合計 55個の属性(property) を収集し、377個のサンプルすべてに同一の項目が含まれていた
-
Layer 1: ブラウザーフィンガープリント
- WebGL関連8属性:
UNMASKED_VENDOR_WEBGL, UNMASKED_RENDERER_WEBGL, WEBGL_debug_renderer_info など
- 画面情報8項目:
colorDepth, pixelDepth, width, height, availWidth, availHeight, availLeft, availTop
- ハードウェア5項目:
hardwareConcurrency, deviceMemory, maxTouchPoints, platform, vendor
- フォント測定4項目: 非表示のdivを作成後、
fontFamily, fontSize, getBoundingClientRect, innerText でレンダリングサイズを測定
- DOM探索8項目:
createElement, appendChild, removeChild, style, position, visibility, ariaHidden など
- ストレージ5項目:
storage, quota, estimate, setItem, usage
- 結果を
localStorage のキー 6f376b6560133c2c に保存し、ページ再読み込みをまたいで保持する
-
Layer 2: Cloudflareネットワーク
- エッジヘッダー5項目:
cfIpCity, cfIpLatitude, cfIpLongitude, cfConnectingIp, userRegion
- これらの値はCloudflareネットワーク経由でのみ存在し、オリジンサーバーへ直接アクセスするボット では欠落または不一致が発生する
-
Layer 3: アプリケーション状態
- React内部構造3項目:
__reactRouterContext, loaderData, clientBootstrap
- これらはChatGPTのReactアプリケーションが完全にレンダリングされ、SSRハイドレーションが完了した場合にのみ存在 する
- HTMLだけを読み込む、JSバンドルを実行しないヘッドレスブラウザー、あるいはReactを実際には実行しないボットフレームワークは失敗する
トークン生成過程
- 55個の属性を収集した後、プログラムは 116バイトの暗号化ブロブ を復号して4つの最終命令を実行する
JSON.stringify(fingerprint) → store → XOR(json, key) → RESOLVE
- 結果値は
OpenAI-Sentinel-Turnstile-Token ヘッダーに変換され、すべての会話リクエストに含まれる
Sentinelの追加構成要素
- Turnstile のほかに2つの追加検証モジュールが存在する
-
Signal Orchestrator
- 271命令で構成
keydown, pointermove, click, scroll, paste, wheel イベントリスナーを設置
window.__oai_so_* 属性36個を追跡し、キー入力タイミング、マウス速度、スクロールパターン、アイドル時間、貼り付けイベント などを監視する
- フィンガープリント収集に加え、行動ベースの生体認証レイヤー として機能する
-
Proof of Work
- 25フィールドのフィンガープリント + SHA-256 hashcash ベース
- 難易度は400K〜500K範囲の一様乱数で、72%が5ms以下で解決された
ai, createPRNG, cache, solana, dump, InstallTrigger, data など7つのバイナリ検出フラグを含む(100サンプルすべて0)
- 計算コストは追加するが、主要な防御手段ではない
トークンを復号可能な主体とセキュリティ上の意味
- 内部プログラムの XOR キーはサーバーが生成してバイトコードに含めるため、
turnstile.dx を生成したサーバーだけがキーを知っている
- ユーザーとシステム運営者の間のプライバシー境界は、暗号学的制約ではなくポリシー上の判断 によって定義される
- 難読化の目的は
- フィンガープリント収集項目を静的解析から隠す
- Webサイト運営者(OpenAI)が生のフィンガープリント値を直接読めないようにする
- 各トークンを固有化して 再利用(replay) を防ぐ
- Cloudflareが検査項目を変更しても外部から認識しにくくする
- しかし暗号化は 同一データストリーム内のキーと XOR 演算 によって行われており、解析防止レベルの難読化 にすぎない
収集および分析統計
| 項目 |
値 |
| 復号されたプログラム |
377/377 (100%) |
| 観測されたユニークユーザー |
32 |
| プログラムあたりの属性数 |
55(すべて同一) |
| 命令数 |
417–580(平均480) |
| XORキー(50サンプル) |
41個 |
| Signal Orchestrator属性 |
36個 |
| Proof of Workフィールド |
25個 |
| PoW解決時間 |
72%が5ms以下 |
分析手法
- 適法な手続きで収集されたトラフィック のみを使用
- 個人ユーザーデータは公開されていない
- すべてのトラフィックは 参加者の同意のもとで観測 された
- Sentinel SDK(
sdk.js, 1,411行)に対して 手動のデオブスケーションおよびオフライン復号 を実施
- 復号はPythonを用いてオフライン環境で進められた
1件のコメント
Hacker Newsのコメント
こんにちは、私はOpenAIのIntegrityチームで働くNickです
今回のチェックは、ボット、スクレイピング、詐欺などのプラットフォーム悪用防止のための保護措置の一部です
無料ユーザーおよび未ログインユーザーにも引き続きアクセスを提供するため、GPUリソースを実際のユーザーに優先配分する目的があります
ページ読み込み時間、最初のトークンまでの時間、ペイロードサイズなどを監視し、保護措置のオーバーヘッド最小化に注力しています
大半のユーザーへの影響はごく小さく、ごく一部だけが多少の遅延を経験する可能性があります
また、誤検知率を下げつつ悪用を難しくするための精度改善も継続的に評価しています
Nickの説明は理解できるが、プライバシーと機能性のどちらかを選ばなければならない世界が今後も続くのか気になる
入力遅延、レンダリングの引っかかり、完全なフリーズまで起きる
iPhone 16のSafariとMacBook Pro M3のChromeで同様に発生している
Cloudflareのプライバシー侵害ツールを回して結果を共有してみては、という半分冗談の提案もある
私はPro契約者で、チーム全体では毎月2,000ドル以上使っている
しかし、VPN(Mullvad) を使うと、ログイン済みでもChatインターフェースが頻繁に切断されたりタイムアウトしたりする
有料ユーザーなら、VPN使用の有無に関係なく安定して利用できるとうれしい
Cloudflareが「疑わしい」ブラウザやIPを理由に、Webをほとんど使えなくしているという不満
Firefoxを使っているだけでCAPTCHA地獄に落ちたと表現している
VPN、プライバシー重視ブラウザ、珍しいIP帯域など、プライバシーを重視するユーザーほどむしろ引っかかりやすい
当のボットはこうしたフィルターを簡単に回避する
自分はFirefoxを使っているが、見るのはせいぜい普通レベルのCAPTCHAだけだという
CGNATを無効にしているので、それが違いを生んでいるのか気になる
銀行で本人確認は済ませているのに、サイトが人間かどうかを見分けられないのは理解しがたいという
OpenAIは未ログインユーザーにも無料のChatGPTを提供しているため、これを無料APIのように悪用されないよう守ろうとしているようだ
AndroidアプリではPlay Integrityチェックが動作するが、Claudeアプリはログインだけを要求し、この種の検証はない
Cookieもアカウントもない状態で質問を入力したら返答が来て驚いたという
APIと比べてサブスクリプション料金ははるかに安いため、悪用防止が必要だ
SPAのロードチェック程度は大きな障壁ではなく、それを認識している人ならすでに回避技術を持っている
他社もそれぞれアンチボットシステムを構築している
ChatGPTのReactアプリが完全にレンダリングされないと特定の属性が存在しない、という点が興味深い
これはブラウザレベルではなく、アプリケーションレイヤーのボット検知という方式だ
たいていの高度な検知システムはこう動いていると思っていたが、今回の発見に特別な意味があるのか気になる
投稿者がなぜこの問題を重要視すべきなのか、もっと明確に説明してほしかった
結局のところ、OpenAIはユーザーに公式のReactアプリを使ってほしいという話であり、それがなぜ問題なのかよく分からない
技術的な観点では興味深い
Turnstileは基本的にサイトごとの設定がないものだと認識しているが、OpenAIがどうやってTurnstileのデータを自社APIと結び付けたのか気になる
なぜボット運営者がただWindows 11 VM + Chromeを回さないのか理解できない、という意見
メモリ重複排除を使えば50台のVMを同時に回せて、AWS基準でページロード1000回あたり1セント程度なので非常に安いという
IPローテーション、位置情報スプーフィング、言語設定、パーサー内蔵など選択肢は豊富で、乗り換えコストも低いため自前で構築する理由はあまりない
単純な手法よりはるかに高くつくからだ
提案のようにWindowsではなくLinuxコンテナを使えば、もっと軽量で効率的だ
ChatGPTが書いたような雑な記事だという批判
2023〜2024年にKeepChatGPTという拡張機能を使っていた
APIなしでもユーザーを装って動く方式が興味深かった
その後、Geminiの登場と頻繁なエラーで使うのをやめたが、AIパネルを右側に移動するオプションは気に入っていた
KeepChatGPT GitHubリンク
この方式はOpenAIのアンチボットシステムにまったく引っかからない
Cloudflareとアプリ間の統合が、標準のTurnstileを超えたカスタム機能なのか、それともエンタープライズ専用なのか気になるという質問