27 min readReact,

Security,

CodingWhile React Server Components rely on the custom Flight protocol to stream interactive UIs, this same mechanism introduces powerful deserialization sinks that attackers can exploit. Durgesh Pawar breaks down the mechanics behind the CVSS 10.0 “React2Shell” vulnerability to show how protocol manipulation can lead to remote code execution. It also covers a practical, ranked set of defenses, from strict schema validation to CSRF hardening, for securing React applications against these structural risks.React Server Components don’t send HTML to your browser. They don’t send JSON either. When a server component renders, what actually travels over the wire is a custom streaming protocol called Flight. It’s a line-delimited format with its own type system, its own reference resolution, and its own rules for reconstructing executable behavior on the client.Most React developers have never opened the Network tab and actually looked at a Flight payload. It looks like a mix of JSON fragments, dollar-sign-prefixed references, and module pointers that the React runtime silently reassembles into a live component tree. The framework handles it, so nobody questions it.I’m not sure most teams have thought carefully about what that trust actually implies.I started pulling apart the Flight protocol after CVE-2025-55182 dropped in December 2025. The security community called it React2Shell, and for good reason. It was a CVSS 10.0, unauthenticated remote code execution vulnerability sitting in the Flight deserialization layer. One crafted HTTP request to a Server Function endpoint, and an attacker had shell access. No credentials needed.The federal Cybersecurity & Infrastructure Agency (CISA) added it to the Known Exploited Vulnerabilities catalog. Sysdig tied in-the-wild exploitation to North Korean state-sponsored actors deploying file-less implants through the Ethereum blockchain. That’s the kind of CVE that gets your attention.After spending time in the source (mostly getOutlinedModel and getChunk, which is where the resolution logic that matters actually lives), I realized React2Shell wasn’t a one-off parsing bug. It was a symptom. Flight reconstructs executable references, lazy-loaded components, server RPC endpoints, and async state from a stream of text. That’s a deserialization system.The attack surface extends well beyond a single missing hasOwnProperty check. This article covers how Flight works on the wire, where the deserialization sinks are, what attackers have already weaponized, and what’s still exposed.This leads to a ranked, practical set of defenses for your own Server Components: schema validation on every Server Action, the server-only package, cross-site request forgery (CSRF) hardening beyond framework defaults, and an assessment of what the Taint API and Web Application Firewalls (WAFs) provide.Table of ContentsFlight On The WireWhy Flight Is A Deserialization SinkThe Mechanics Of React2ShellThe FixDefenses, Ranked By ImpactWhat Came After React2ShellWhat’s Still ExposedThis Has Happened BeforeWhere This Goes NextFlight On The WireOpen your browser’s Network tab on any Next.js App Router page and look for requests returning Content-Type: text/x-component. That’s Flight. It’s not a single JSON blob. It’s a streaming, line-delimited format where each line is a self-contained “row” that the client-side React runtime processes as it arrives over the connection.Here’s what a simple Flight payload looks like in practice:1:I["./src/components/ClientComponent.js",["chunks/main.js"],"default"]