The React ecosystem has undergone a profound transformation with the arrival of Server Components and the Flight protocol. This real-time communication system between server and client promises efficiency and performance, but it has also opened the door to critical vulnerabilities that no development team should ignore. In this article, we analyze how Flight turns structured data into attack vectors, the most serious documented incidents, and the measures organizations can take to protect their applications.
The Flight protocol is not a simple JSON format. It is a streaming system that transports module references, server actions, asynchronous promises, and interface elements. Each line of the stream contains an instruction that the React runtime interprets to rebuild the component tree on the client. This means the server sends not only data, but executable behavior. And when an attacker manages to manipulate that stream, the consequences can be catastrophic.
To understand the risk, we must look at the $ prefix system. When the parser encounters a string starting with $, it interprets it as a reference to another fragment of the stream. For example, $2 points to fragment 2, while $1:user:name indicates it should access the user.name property of fragment 1. This property traversal, known as $:, allows navigating any object, including prototypes. In JavaScript systems, accessing __proto__ or constructor can lead to modification of shared prototypes, opening the door to prototype pollution attacks.
The most notorious vulnerability was CVE-2025-55182, dubbed React2Shell, which achieved a CVSS score of 10.0. An unauthenticated attacker could send a single HTTP request to a Server Function endpoint and gain full shell access. The mechanism combined several protocol features: prototype traversal to reach the Function constructor, self-referencing internal chunks with $@, Thenable hijacking, and triggering through the blob handler $B. All this demonstrated that Flight not only serializes data but reconstructs behavior in a dangerous way.
After the patch was released, the React team fixed the issue by adding a hasOwnProperty check on every property access. However, the fundamental structure of the protocol remains the same: property traversal via $: is still intact, only now steps are validated. For many cybersecurity experts, this represents a symptomatic patch and not a structural redesign. The latent risk is that future vulnerabilities could emerge from the same pattern.
In addition to the RCE, other vulnerabilities were documented in the same ecosystem. CVE-2025-55183 allowed source code exposure when a Server Action serialized a malicious argument. CVE-2025-55184 and CVE-2025-67779 were denial of service due to infinite promise recursion. CVE-2026-23864 exploited unbounded request body buffering to exhaust memory. Finally, CVE-2026-27978 was a CSRF bypass that occurred when the browser sent Origin: null from a sandboxed iframe, and Next.js treated it as a missing origin rather than a cross-origin one.
Given this scenario, companies developing applications with React Server Components must adopt defense in depth. The first and most effective measure is input validation on every Server Action. Using libraries like Zod or Valibot to define strict schemas and executing safeParse before any business logic prevents malformed data from reaching the deserialization engine. It is crucial not to destructure arguments before validation, because that already involves accessing properties on untrusted data.
The server-only package is another simple but powerful barrier. By importing it in files containing credentials, database queries, or internal logic, it ensures that no client component can import them directly or transitively. However, care must be taken with barrel files that re-export functions; if a barrel mixes server and client code, the protection is diluted.
CSRF protection requires going beyond framework defaults. Configuring cookies with SameSite=Strict, generating explicit CSRF tokens for sensitive operations, and avoiding at all costs adding 'null' to the allowed origins list are essential practices. The Next.js error that allowed the bypass with Origin: null was fixed in version 16.1.7, but many applications still run older versions.
React's Taint API, with functions like taintObjectReference and taintUniqueValue, offers a development-time security layer. It allows marking objects or strings so that the serializer throws an error if they attempt to cross to the client. However, its effectiveness is limited: it only works with references to the same object, not copies, destructuring, or transformations. It is a useful guardrail, but not a firewall.
WAFs (Web Application Firewalls) can add superficial detection of known patterns, such as constructor:constructor or __proto__ strings. However, sophisticated attackers pad the payload with junk data before the malicious part to evade inspection, or use chunked encoding. Relying solely on a WAF is dangerous; it must be combined with the previous defenses.
Beyond patches, there are structural risks that persist. Man-in-the-middle (MITM) attacks on the Flight stream are viable if an attacker controls the network (compromised CDN, poisoned cache). They can modify $I rows to redirect module loading, inject $F references to activate hidden RPCs, or alter props leading to XSS if the component uses dangerouslySetInnerHTML. The $ prefix escaping applied by the serializer only protects against user data, not against an attacker writing directly into the stream.
Server Action enumeration is another vector. The server-reference-manifest.json file contains the map of all actions with their hash IDs. If that manifest is exposed due to misconfigured hosting or a path traversal, the attacker obtains a complete catalog of RPC endpoints and can perform IDOR or parameter tampering attacks.
Encrypted closure tampering is also concerning. When a Server Action captures variables from its scope, Next.js encrypts them with an AES key. If the attacker gains file read access (via SSRF or path traversal), they can extract the key, decrypt, modify, and re-encrypt the state, making the server accept forged data.
Finally, there is the possibility of activating dormant modules via module IDs. If a compromised package is present in node_modules as a transitive dependency but never imported directly, the bundler includes it in a chunk but does not load it. An attacker who can inject an $I row into the Flight stream could force the loading of that chunk, activating code that would otherwise remain asleep.
These types of vulnerabilities are not new. Google Web Toolkit, Java Server Faces, and ASP.NET already suffered similar problems with their custom serialization formats. The lesson is always the same: when a framework invents a communication protocol that carries executable behavior, the attack surface expands beyond what developers usually anticipate. Blind trust that the server is the sole producer of that data is unsustainable.
For companies building custom software with modern technologies, cybersecurity must be an integral part of the development process. Simply applying patches is not enough; architectures must be designed to limit the impact of any future vulnerability. At Q2BSTUDIO, we accompany our clients throughout the software lifecycle, from risk analysis to the implementation of defense in depth, including integration with AWS/Azure cloud services, artificial intelligence systems, and Business Intelligence with Power BI. In addition, we develop AI agents that automate monitoring and incident response tasks, reducing the exposure window.
The security community has shown that the Flight protocol, by its very nature, will remain an attractive target. As long as mechanisms for cryptographic validation of serialized streams, signed component trees, or content integrity checks are not implemented, applications based on Server Components will depend on the constant vigilance of their development teams and collaboration with security experts. Investment in prevention and team training is the only guarantee against threats that evolve as fast as the technology we are trying to protect.




