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 ThisThe 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 tur...
First seen: 2026-03-29 20:58
Last seen: 2026-03-30 15:10