ChatGPTが利用するCloudflareのボット検知システム「Turnstile」の内部構造および関連するSentinelチャレンジに関する詳細な解析結果が、セキュリティ研究やリバースエンジニアリングを扱う技術ブログサイトのBuchodiによって公開されました。ネットワークトラフィックの傍受とSDKの難読化解除を通じて解析が行われ、どのようにボットが検知されトークンが生成されるのかという仕組みが明らかにされています。

ChatGPT Won't Let You Type Until Cloudflare Reads Your React State. I Decrypted the Program That Does It.

https://www.buchodi.com/chatgpt-wont-let-you-type-until-cloudflare-reads-your-react-state-i-decrypted-the-program-that-does-it/

◆Turnstileによる多層的チェック

ChatGPTからの全メッセージはブラウザ内で秘密裏に実行されるCloudflare Turnstileプログラムをトリガーします。このプログラムは単なるブラウザのフィンガープリンティングにとどまらず「ブラウザ」「Cloudflareネットワーク」「ChatGPTアプリケーション自体」の3つのレイヤーにわたる55ものプロパティをチェックします。具体的には、WebGL・画面解像度・ハードウェア情報・フォント測定・DOM操作・ストレージ利用状況といったブラウザ固有の情報に加え、Cloudflareのエッジヘッダー情報も収集されます。さらにChatGPTのReactアプリケーションが完全にレンダリング・ハイドレーションされているかどうかも確認しています。多層的なチェックを行うことによりブラウザのフィンガープリントが偽装されていたとしても実際のChatGPTシングルページアプリケーション(SPA)を正常にレンダリングしないボットは検知され失敗します。



◆復号プロセス

Turnstileのバイトコードは受信時に暗号化されています。まず準備レスポンスに含まれるBase64エンコードされたフィールド(turnstile.dx)は準備リクエストからのトークン(p)とのXOR演算によって復号されます。外側のレイヤーの復号は両者が同じHTTP通信でやり取りされるため比較的容易です。

outer = json.loads(bytes(
base64decode(dx)[i] ^ p_token[i % len(p_token)]
for i in range(len(base64decode(dx)))
))
# → 89 VM instructions

外側のレイヤーを復号することにより約89個の仮想マシン(VM)命令が得られますが、VM命令の中に実際のフィンガープリントプログラムを含む19KBの暗号化されたデータブロックが含まれており、外側のレイヤーとは異なるXORキーを使用します。当初このキーはperformance.now()から派生するものと考えられていましたが、詳細な解析の結果キーはVM命令の中に浮動小数点リテラルとして直接埋め込まれていることが判明しました。キーはサーバーによって生成されバイトコードに埋め込まれ、解析者はキーを知ることで内部プログラムを復号できます。

[41.02, 0.3, 22.58, 12.96, 97.35]

完全な復号チェーンはHTTPリクエストとレスポンスのみで完結し、以下の5ステップで構成されます。

1. 準備リクエストからpを読み取る

2. 準備レスポンスからturnstile.dxを読み取る

3. base64decode(dx)とpをXOR演算し外部バイトコードを取得する

4. 19KBのブロブの後に続く5引数命令を探し、その最後の引数をキーとして取得する

5. base64decode(blob)とキーの文字列表現をXOR演算し、内部プログラム(417〜580 VM命令)を取得する

◆Turnstile以外のSentinelチャレンジ

TurnstileはSentinelシステムが提供する3つのチャレンジのうちの1つであり、 残りの2つは以下の通りです。

・Signal Orchestrator(SO):キーダウン・ポインター移動・クリックなどのユーザーインタラクションイベントリスナーをインストールし、window.__oai_so_*プロパティを監視することでキーストロークのタイミングやマウスの速度といった行動バイオメトリクスを追跡する、行動生体認証のレイヤーです。

・Proof of Work(PoW):25フィールドのフィンガープリントとSHA-256ハッシュキャッシュを組み合わせたものです。計算コストは増大するものの解析によれば7つのバイナリ検出フラグはサンプル全体で一貫してゼロであり、主要な防御策ではない可能性が考えられます。

すべてのSentinelチャレンジに関する分析結果がこちら。

メトリック値補足プログラムの復号377/377100%ユニークユーザー数32 プログラムごとのプロパティ55全サンプルで同一プログラムごとの手順417〜580平均480固有のXORキー41 SOの挙動特性36 PoWフィンガープリントフィールド25 PoW解決時間72%が5ms未満 

◆難読化の意図

XORキーを用いて難読化することにより以下に示す運用上の目的を達成しています。

・静的解析からフィンガープリントチェックリストを隠蔽する

・ウェブサイト運営者(OpenAI)がリバースエンジニアリングなしに生のフィンガープリント値を読み取ることを防ぐ

・各トークンを一意にすることによりリプレイ攻撃を防ぐ

ただし実際には同じデータストリーム内でのXOR演算に過ぎないため、簡単な読み取りであればともかくとして分析を完全に防ぐことはできない、ということが今回の解析で明らかになっています。

◆まとめ

Sentinelチャレンジは以下のチェックを実施することで、ボットによるアクセスの防止に努めていることが示されました。

・Turnstileが多層的なチェックと複雑な復号プロセスによりボットを検知

・Turnstile以外が行動バイオメトリクスやフィンガープリントを検出