If your team ships React Server Components — and by mid-2026 most Next.js, Remix, and Waku projects do — there is a critical attack surface you may not have considered. The React Flight protocol, the custom streaming format that transmits server-rendered component trees to the browser, is not just a data format. It reconstructs executable behaviour from network streams. And that distinction has already been exploited in the wild.
TL;DR
- The React Flight protocol deserialises executable behaviour (module loading, RPC endpoints, lazy components), not just passive data — making it fundamentally different from HTML or JSON
- CVE-2025-55182 (React2Shell, CVSS 10.0) demonstrated unauthenticated remote code execution via a single crafted HTTP request to any Server Function endpoint
- Input validation on every Server Action argument is the single most impactful defence — use Zod or Valibot schemas before any business logic
- Framework patches close known gadget chains but do not redesign the underlying deserialisation model — future vulnerabilities are architecturally likely
- Teams running React Server Components in production should audit their Server Actions immediately and layer defences: validation, CSRF hardening, WAF rules, and the server-only package
What Is the React Flight Protocol?
When a React Server Component renders on the server, the result is not sent as HTML. Instead, React serialises the component tree into a custom streaming format called Flight. Every row follows the same syntax: a row ID, a tag, and a payload. Tags like J (JSON trees), M (modules), I (imports), and F (server references) tell the client-side runtime how to reconstruct the UI.
This is where the security implications begin. Unlike JSON, which is a passive data format, Flight reconstructs executable behaviour. It loads modules, registers RPC endpoints, and resolves lazy components. Every Server Action — those convenient 'use server' functions — becomes a callable RPC endpoint exposed over HTTP. The protocol effectively bridges the gap between server-side logic and client-side execution, and that bridge is precisely what attackers target.
React2Shell: A Single Request to Shell Access
CVE-2025-55182, dubbed React2Shell, demonstrated how devastating this attack surface can be. Rated CVSS 10.0, it exploited Flight’s deserialisation mechanics through a multi-step gadget chain:
- Property traversal to Function constructor: Flight’s
$:prefix enables colon-separated path traversal. Without proper validation, an attacker could traverse to__proto__:constructorto reach JavaScript’s Function constructor. - Internal state exposure: A raw chunk self-reference (
$@0) exposed internal React state, giving the attacker the primitives needed to chain the exploit. - Blob handler invocation: The malicious Function was triggered through the Blob handler, completing the chain.
- Result: Unauthenticated remote code execution — shell access from a single HTTP request.
State-sponsored actors were deploying file-less implants within hours of the vulnerability becoming known. No authentication was required. No user interaction was needed. If your application exposed a Server Function endpoint, it was vulnerable.
The Deeper Problem: Deserialisation by Design
React2Shell was patched in React 19.0.1+ by caching legitimate hasOwnProperty references to block prototype chain traversal. But the architectural concern runs deeper than any single CVE.
The Flight protocol’s fundamental design decision — reconstructing executable behaviour from network streams — means the deserialisation surface is inherently rich. Additional CVEs have followed: DoS variants (CVE-2025-55184, CVE-2025-67779, CVE-2026-23864) requiring patches up to React 19.2.4+, and a CSRF bypass (CVE-2026-27978) exploiting Origin: null headers in sandboxed contexts.
Each patch closes a specific gadget chain. None of them redesign the underlying model. For development teams, this means treating React Server Components security as an ongoing discipline, not a one-time patch-and-forget exercise.
Thenable Hijacking: The Subtler Attack Vector
Beyond prototype pollution, Flight’s async pipeline introduces another class of vulnerability. Objects with manipulated .then properties get automatically awaited by React’s processing pipeline. An attacker who can inject an object with a custom .then method into the deserialisation stream gains arbitrary execution within the async resolution chain.
This is particularly insidious because it exploits a fundamental JavaScript language feature — the thenable protocol — rather than a React-specific bug. Any object that looks like a Promise gets treated like one, and that assumption can be weaponised.
A Ranked Defence Strategy
Based on the current threat landscape, here is how development teams should prioritise their defences:
1. Input Validation on Every Server Action (Critical)
This is the single most impactful thing you can do. Validate every Server Action argument with strict schemas — Zod or Valibot — before any business logic executes. Treat Server Actions exactly like public API endpoints, because that is what they are.
// Every Server Action should start with validation
'use server'
import { z } from 'zod'
const UpdateProfileSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
})
export async function updateProfile(data: unknown) {
const validated = UpdateProfileSchema.parse(data)
// Only now proceed with business logic
}
2. The server-only Package (High)
Install and use the server-only package to create a compile-time boundary that prevents Client Components from importing server-side code. This reduces the attack surface by ensuring sensitive modules cannot be accidentally exposed to the client.
3. CSRF Hardening (High)
Do not rely solely on framework defaults. Layer explicit CSRF tokens, set SameSite=Strict on session cookies, and critically, never whitelist 'null' as a valid origin. CVE-2026-27978 specifically exploited the Origin: null header that browsers send from sandboxed iframes.
4. Keep React Patched (High)
This sounds obvious, but the cadence of Flight-related CVEs means your patch cycle matters. React 19.0.4+, 19.1.5+, and 19.2.4+ each close distinct vulnerability chains. Pin your React version deliberately and monitor the React security advisories.
5. WAF Rules (Medium)
Configure your Web Application Firewall to detect payloads containing __proto__ chains and constructor:constructor patterns. This is a defence-in-depth measure — it will not catch novel gadget chains, but it raises the bar against known exploit patterns.
6. React Taint API (Low)
The experimental Taint API provides development-time guards that track object references to prevent sensitive data serialisation. Its effectiveness against sophisticated attacks is limited, but it adds another layer of awareness during development.
What This Means for Your Team
The shift to React Server Components has been one of the most significant architectural changes in frontend development. But with that shift comes a security model that many teams have not fully reckoned with. Server Actions are not just convenient abstractions — they are publicly accessible RPC endpoints that deserialise complex, executable payloads.
If your team is building with Next.js App Router, Remix with RSC, or any framework leveraging the Flight protocol, a security audit of your Server Actions should be a priority. Not next quarter. Now.
At REPTILEHAUS, we work with teams across Dublin and beyond to secure their web applications — from architecture review through to production hardening. If you are running React Server Components in production and want an expert assessment of your exposure, get in touch.
📷 Photo by Markus Spiske on Unsplash


