Buchodi reverse-engineered Cloudflare's obfuscated Turnstile script and discovered it traverses React's internal fiber tree — including component state, props, and hooks — before users can even interact with ChatGPT. The forensic teardown demonstrates that bot detection is reaching far deeper into application internals than most developers realize, fingerprinting the app's internal structure as part of its bot-vs-human determination.
The editorial argues that both the alarmed camp and the dismissive camp miss the deeper issue: React's fiber tree is attached to the DOM via __reactFiber$ properties, making it trivially accessible to any JavaScript on the page — analytics, ad networks, chat widgets, or bot detection. The real problem is that almost nobody treats this exposure as a security surface, despite it being inherent to how React works.
As noted in the editorial synthesis, a significant faction in the 593-comment HN discussion argued that Cloudflare's fiber tree inspection is not fundamentally different from reading the DOM — any script on a page already has full access to page content. They see the technique as a pragmatic bot-detection approach rather than an alarming intrusion into application internals.
As described in the editorial synthesis, another major faction in the HN discussion was alarmed that a CDN and bot-detection provider is reading React's internal component state, props, and hooks. Their concern is that this represents a CDN overstepping its role — sites embed Cloudflare for protection, not to have their application's internal data structures fingerprinted and inspected by a third party.
A security researcher writing under the handle Buchodi published a forensic teardown of the obfuscated JavaScript that Cloudflare injects on OpenAI's ChatGPT. The finding: before users can type a single character into the chat input, Cloudflare's Turnstile bot-detection script traverses React's internal fiber tree — the data structure React uses to track component state, props, hooks, and the virtual DOM. The script was heavily obfuscated, but Buchodi reverse-engineered it to reveal that it fingerprints the application's internal structure as part of its bot-vs-human determination.
The post hit 918 points on Hacker News, and the discussion quickly split into two camps: those alarmed that a CDN provider is reading application internals, and those pointing out that this is technically just "reading the DOM with extra steps." Both camps are missing the more important point: React's fiber tree has become a de facto public API that any script on your page can inspect, and almost nobody treats it that way.
React's reconciliation engine maintains a fiber tree — a linked list of nodes representing every component in your application. Each fiber node contains the component's type, its current state, pending state updates, props, refs, hooks (stored as a linked list on the fiber's `memoizedState`), and pointers to parent, child, and sibling fibers. This tree is attached to the DOM: every rendered DOM element has a property starting with `__reactFiber$` (or `__reactInternalInstance$` in older versions) that provides direct access to its corresponding fiber node.
Any JavaScript running on the page — analytics scripts, ad networks, chat widgets, bot detection — can call `Object.keys(element)` on any DOM node, find the React fiber key, and walk the entire component tree from there. This isn't an exploit. It's how React works. The fiber tree is reachable from the DOM by design, because React needs to associate DOM elements with their component state during event handling.
What Cloudflare's script appears to do is use this fiber traversal as a fingerprinting signal. A real React application will have a fiber tree with specific structural properties — depth, branching patterns, hook counts, state shapes — that are difficult for a headless browser or simple bot to replicate convincingly. It's clever bot detection: instead of just checking mouse movements or solving CAPTCHAs, it verifies that the page is running a genuine React application with real component state.
The Buchodi analysis focused on ChatGPT, but the architectural exposure applies to every React application on the internet. Consider what a third-party script can extract by walking the fiber tree:
User state. If your app stores user data in component state — email addresses, authentication tokens, form inputs, preferences — any script on the page can read it. React Server Components and Next.js server actions don't help here; the client-side state tree is still fully exposed for any component that hydrates.
Application structure. The fiber tree reveals your component hierarchy, which effectively maps your application's architecture. Component names (unless minified), prop structures, and state shapes are all visible. For competitive intelligence or vulnerability discovery, this is a goldmine.
Hook internals. Every `useState`, `useEffect`, `useRef`, and `useMemo` call creates entries in the fiber's hook linked list. A script can enumerate your hooks, read their current values, and even infer your application's behavior patterns from the effect dependency arrays.
This isn't theoretical. Browser extensions have been reading fiber trees for years — React DevTools is literally built on this capability. The difference is that React DevTools runs with the user's explicit permission. Third-party scripts injected via tag managers, CDN providers, or ad networks do not.
Content Security Policy, the standard defense against unwanted script behavior, doesn't help here. CSP controls which scripts can *execute* on your page and where they can *send data*. It cannot restrict what an allowed script does once it's running. If your CSP permits Cloudflare's challenge script (which it must, if you're using Cloudflare for DDoS protection), that script has full access to the DOM and everything attached to it — including React fiber internals.
The fundamental issue is that the web security model grants every permitted script equal access to the entire page context. There's no isolation boundary between "scripts I trust with my application state" and "scripts I trust to load fonts." The `
Some teams use `Object.defineProperty` to make the React fiber keys non-enumerable or to trap access with a Proxy. This is a cat-and-mouse game: a determined script can still find fibers by brute-forcing property names or hooking `React.createElement` before the application loads.
The practical response isn't paranoia — it's treating client-side state with the same skepticism you apply to client-side validation.
Audit your third-party scripts against your state model. List every script that runs on your page (tag managers make this harder than it should be). For each one, ask: if this script read my entire React state tree, what would it learn? If the answer includes auth tokens, PII, or business-sensitive data, you have a problem regardless of Cloudflare.
Move sensitive data out of React state. Authentication tokens belong in `httpOnly` cookies, not in `useState`. User PII should be fetched on demand and cleared after use, not cached in component state. This is defense in depth — the fiber tree can still reveal structure, but it shouldn't reveal secrets.
Consider component name minification seriously. Production builds with Terser or similar tools can strip component display names, making fiber tree traversal less informative. React's `displayName` property is a debugging convenience that becomes a liability when untrusted scripts can read it. If you're shipping readable component names like `
Monitor for fiber tree access. You can instrument `Object.getOwnPropertyNames` or set up a `Proxy` on key DOM elements to detect when scripts enumerate React-internal properties. This won't prevent access, but it gives you visibility into which scripts are reading your fiber tree and how often.
The Cloudflare-ChatGPT case is a symptom of a deeper architectural tension in web development: the DOM was designed as a shared, traversable document tree in an era when pages had one author. Modern web applications are multi-author environments — your code, your CDN's code, your analytics vendor's code, your ad network's code — all running in a single, shared execution context with no isolation. React's fiber tree is just the most legible example of how much application state is accessible to every participant. Until browsers ship meaningful in-page script isolation (and proposals like Realms and Compartments remain far from standardized), the only honest security posture is: everything on the client is public. Build accordingly.
It's absurd how unusable Cloudflare is making the web when using a browser or IP address they consider "suspicious". I've lately been drowning in captchas for the crime of using Firefox. All in the interest of "bot protection", of course.
Presumably this is all because OpenAI offers free ChatGPT to logged out users and don't want that being abused as a free API endpoint.
> 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 ha
Perhaps the author should have made it clearer why we should care about any of this. OpenAI want you to use their real react app. That’s… ok? I skimmed the article looking for the punchline and there doesn’t seem to be one.
Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.
Hey! I'm Nick, and I work on Integrity at OpenAI. These checks are part of how we protect our first-party products from abuse like bots, scraping, fraud, and other attempts to misuse the platform.A big reason we invest in this is because we want to keep free and logged-out access available for