NOW LET US – AI RAG SaaS Studio TP.HCM
NOW LET US
Digital Product Studio
Back to news
DEV-TOOLS...5 min read

ChatGPT Won't Let You Type Until Cloudflare Reads Your React State

Share
NOW LET US Article – ChatGPT Won't Let You Type Until Cloudflare Reads Your React State

A technical deep dive reveals how Cloudflare Turnstile secures ChatGPT by inspecting React's internal state. Beyond standard fingerprinting, the system verifies full application hydration to effectively block sophisticated bots.

Every ChatGPT message triggers a Cloudflare Turnstile program that runs silently in your browser. I decrypted 377 of these programs from network traffic and found something that goes beyond standard browser fingerprinting.

The program checks 55 properties spanning three layers: your browser (GPU, screen, fonts), the Cloudflare network (your city, your IP, your region from edge headers), and the ChatGPT React application itself (__reactRouterContext, loaderData, clientBootstrap). Turnstile doesn't just verify that you're running a real browser. It verifies that you're running a real browser that has fully booted a specific React application.

A bot that spoofs browser fingerprints but doesn't render the actual ChatGPT SPA will fail.

The Encryption Was Supposed to Hide This

The Turnstile bytecode arrives encrypted. The server sends a field called turnstile.dx in the prepare response: 28,000 characters of base64 that change on every request.

The outer layer is XOR'd with the p token from the prepare request. Both travel in the same HTTP exchange, so decrypting it is straightforward:

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

Inside those 89 instructions, there is a 19KB encrypted blob containing the actual fingerprinting program. This inner blob uses a different XOR key that is not the p token.

Initially I assumed this key was derived from performance.now() and was truly ephemeral. Then I looked at the bytecode more carefully and found the key sitting in the instructions:

[41.02, 0.3, 22.58, 12.96, 97.35]

The last argument, 97.35, is the XOR key. A float literal, generated by the server, embedded in the bytecode it sent to the browser. I verified this across 50 requests. Every time, the float from the instruction decrypts the inner blob to valid JSON. 50 out of 50.

The full decryption chain requires nothing beyond the HTTP request and response:

  1. Read p from prepare request
  2. Read turnstile.dx from prepare response
  3. XOR(base64decode(dx), p) → outer bytecode
  4. Find the 5-arg instruction after the 19KB blob → last arg is the key
  5. XOR(base64decode(blob), str(key)) → inner program (417-580 VM instructions)

The key is in the payload.

What the Decrypted Program Checks

Each inner program uses a custom VM with 28 opcodes (ADD, XOR, CALL, BTOA, RESOLVE, BIND_METHOD, JSON_STRINGIFY, etc.) and randomized float register addresses that change per request. I mapped the opcodes from the SDK source (sdk.js, 1,411 lines, deobfuscated).

The program collects 55 properties. No variation across 377 samples. All 55, every time, organized into three layers:

Layer 1: Browser Fingerprint

WebGL (8 properties): UNMASKED_VENDOR_WEBGL, UNMASKED_RENDERER_WEBGL, WEBGL_debug_renderer_info, getExtension, getParameter, getContext, canvas, webgl

Screen (8): colorDepth, pixelDepth, width, height, availWidth, availHeight, availLeft, availTop

Hardware (5): hardwareConcurrency, deviceMemory, maxTouchPoints, platform, vendor

Font measurement (4): fontFamily, fontSize, getBoundingClientRect, innerText. Creates a hidden div, sets a font, measures rendered text dimensions, removes the element.

DOM probing (8): createElement, appendChild, removeChild, div, style, position, visibility, ariaHidden

Storage (5): storage, quota, estimate, setItem, usage. Also writes the fingerprint to localStorage under key 6f376b6560133c2c for persistence across page loads.

Layer 2: Cloudflare Network

Edge headers (5): cfIpCity, cfIpLatitude, cfIpLongitude, cfConnectingIp, userRegion

These are injected server-side by Cloudflare's edge. They exist only if the request passed through Cloudflare's network. A bot making direct requests to the origin server or running behind a non-Cloudflare proxy will produce missing or inconsistent values.

Layer 3: Application State

React internals (3): __reactRouterContext, loaderData, clientBootstrap

This is the part that matters. __reactRouterContext is an internal data structure that React Router v6+ attaches to the DOM. loaderData contains the route loader results. clientBootstrap is specific to ChatGPT's SSR hydration.

These properties only exist if the ChatGPT React application has fully rendered and hydrated. A headless browser that loads the HTML but doesn't execute the JavaScript bundle won't have them. A bot framework that stubs out browser APIs but doesn't actually run React won't have them.

This is bot detection at the application layer, not the browser layer.

The Exit: How the Token Is Built

After collecting all 55 properties, the program hits a 116-byte encrypted blob that decrypts to 4 final instructions:

[
[96.05, 3.99, 3.99], // JSON.stringify(fingerprint)
[22.58, 46.15, 57.34], // store
[33.34, 3.99, 74.43], // XOR(json, key)
[1.51, 56.88, 3.99] // RESOLVE → becomes the token
]

The fingerprint is JSON.stringify'd, XOR'd, and resolved back to the parent. The result is the OpenAI-Sentinel-Turnstile-Token header sent with every conversation request.

What Else Sentinel Runs

Turnstile is one of three challenges. The other two:

Signal Orchestrator (271 instructions): Installs event listeners for keydown, pointermove, click, scroll, paste, and wheel. Monitors 36 window.__oai_so_* properties tracking keystroke timing, mouse velocity, scroll patterns, idle time, and paste events. A behavioral biometric layer running underneath the fingerprint.

Proof of Work (25-field fingerprint + SHA-256 hashcash): Difficulty is uniform random (400K-500K), 72% solve under 5ms. Includes 7 binary detection flags (ai, createPRNG, cache, solana, dump, InstallTrigger, data), all zero across 100% of 100 samples. The PoW adds compute cost but is not the real defense.

Who Can Decrypt the Token

The XOR key for the inner program is a server-generated float embedded in the bytecode. Whoever generated the turnstile.dx knows the key. The privacy boundary between the user and the system operator is a policy decision, not a cryptographic one.

The obfuscation serves real operational purposes: it hides the fingerprint checklist from static analysis, prevents the website operator (OpenAI) from reading raw fingerprint values without reverse-engineering the bytecode, makes each token unique to prevent replay, and allows Cloudflare to change what the program checks without anyone noticing.

But the "encryption" is XOR with a key that's in the same data stream. It prevents casual inspection. It does not prevent analysis.

The Numbers

| Metric | Value | |---|---| | Programs decrypted | 377/377 (100%) | | Unique users observed | 32 | | Properties per program | 55 (identical across all samples) | | Instructions per program | 417-580 (mean 480) | | Unique XOR keys (50 samples) | 41 | | SO behavioral properties | 36 | | PoW fingerprint fields | 25 | | PoW solve time | 72% under 5ms |

Methodology

No systems were accessed without authorization. No individual user data is disclosed. All traffic was observed from consented participants. The Sentinel SDK was beautified and manually deobfuscated. All decryption was performed offline using Python.

© 2026 Now Let Us. All rights reserved.

Source: Hacker News

Advertisement
Ad slot ready: 5887729102

More in this category

NOW LET US Related – GLM 5.2 Is Out

dev-tools

GLM 5.2 Is Out

Zhipu AI has officially released GLM-5.2, its most powerful open-source model to date, featuring a 1M context window and advanced long-horizon task capabilities. The release underscores Zhipu's commitment to open-source AI and global scientific collaboration amid rising technological restrictions.

NOW LET US Related – Noise infusion banned from statistical products published by Census Bureau

dev-tools

Noise infusion banned from statistical products published by Census Bureau

The U.S. Department of Commerce has banned "noise infusion" from statistical products published by the Census Bureau, a decision that could have severe consequences for both data utility and privacy protection.

NOW LET US Related – Treating pancreatic tumours may have revealed cancer's master switch

dev-tools

Treating pancreatic tumours may have revealed cancer's master switch

A promising new drug called daraxonrasib has shown breakthrough results in treating pancreatic cancer, doubling median survival times. This achievement could pave the way for an entirely new class of cancer treatments.

NOW LET US Related – Every Frame Perfect

dev-tools

Every Frame Perfect

In UI design, perfection isn't just about the start and end states, but every single transition frame in between. Polishing these micro-interactions is key to building user trust.

NOW LET US Related – Leaving Mozilla

dev-tools

Leaving Mozilla

A poignant and candid reflection from a 15-year Mozilla veteran upon their departure. The author highlights the leadership's missteps in trying to emulate tech giants and urges Mozilla to return to its core values: community and uniqueness.

NOW LET US Related – Shepherd's Dog: A Game by the Most Dangerous AI Model

dev-tools

Shepherd's Dog: A Game by the Most Dangerous AI Model

A developer tested Anthropic's latest, supposedly 'too dangerous' AI model by asking it to build a long-held game idea in a single shot. The model succeeded, generating a complete 2,319-line game after a 45-minute reasoning session.

EXPLORE TOPICS

Discover All Categories

Deep dive into the specific technology sectors that matter most to you.