O Payload Que Ninguém Inspeciona
E aí, dev! Abre a aba Network em qualquer página do Next.js App Router e filtra por Content-Type: text/x-component. O que você vê não é JSON. É o Flight — o protocolo de streaming line-delimited do React, com seu próprio sistema de tipos, sua própria resolução de referências e suas próprias regras para reconstruir comportamento executável no cliente. 🚀
A maioria dos devs React nunca olha. O framework resolve, então ninguém questiona o que essa confiança implica. Aí em dezembro de 2025, o CVE-2025-55182 caiu — um RCE não autenticado de CVSS 10.0 na camada de desserialização do Flight. A CISA colocou no catálogo de vulnerabilidades exploradas. A Sysdig ligou a exploração in-the-wild a atores estado-nação da Coreia do Norte deployando implantes file-less via blockchain Ethereum.
Isso não é um bug de parsing isolado. É sintoma. O Flight reconstrói referências executáveis, componentes lazy-loaded, endpoints RPC e estado assíncrono a partir de um stream de texto. Isso é um sistema de desserialização — e herda todos os riscos clássicos de desserialização.

Flight On The Wire: O Sistema de Prefixo $
Cada linha do Flight segue ID:TAG:PAYLOAD. O interessante é o sistema de prefixo $ — quando o parser do cliente encontra uma string começando com $, ele não trata como texto literal. Ele roteia por um caminho de resolução específico em parseModelString.
| Prefixo | Tipo | Comportamento do Parser |
|---|---|---|
$ | Model Reference | Resolve para outro chunk no stream |
$: | Property Access | Navega nas propriedades de um chunk resolvido ($1:user:name) |
$F | Server Reference | Representa uma Server Action chamável (RPC) |
$L | Lazy Component | Adia o carregamento do componente |
$@ | Raw Chunk | Retorna o wrapper interno Chunk (Thenable) |
$B | Blob/Binary | Dispara o handler de blob |
O $: é o crítico para exploração. Ele permite property traversal arbitrário dirigido por dados no stream. Se você já auditou JavaScript para prototype pollution, esse padrão é familiar.
A Gadget Chain do React2Shell
A causa raiz ficava em getOutlinedModel, a função que resolve paths profundos de referências $::
// Loop vulnerável — sem verificação hasOwnProperty
for (key = 1; key < reference.length; key++)
parentObject = parentObject[reference[key]];
O atacante manda $1:__proto__:constructor:constructor, e o loop sobe de um objeto JSON até Object.prototype, depois Function. Function("código arbitrário")() executa. Sem allowlist, sem check de __proto__.
A partir daí, a chain compõe features legítimas do Flight:
- Prototype walk até Function via
$: - Raw chunk self-reference via
$@0para pegar um handle interno mutável - Thenable hijack — seta
.thenparaChunk.prototype.then - Context confusion — sobrescreve
_response._formData.getapontando paraFunction - Trigger via
$B0— blob handler chamaFunction("comando_atacante")()
Cada passo é uma feature legítima. A vulnerabilidade emerge da composição. Olha só isso! 👀

Defesas, Ranqueadas por Impacto
1. Validação de Schema em Toda Server Action
A defesa mais impactante no nível de aplicação. Valide com Zod ou Valibot no topo de cada Server Action — antes de logar, antes de qualquer lógica. Use .safeParse(), não .parse().
"use server"
import { z } from "zod"
const CommentSchema = z.object({
postId: z.string().uuid(),
body: z.string().min(1).max(5000),
})
// Bom: valida o argumento bruto primeiro
export async function addComment(data) {
const parsed = CommentSchema.safeParse(data)
if (!parsed.success) return { error: "Invalid input" }
await db.comments.create(parsed.data)
}
Se sua Server Action não começa com um schema parse, trate como vulnerabilidade. Considere uma regra ESLint customizada que flagra qualquer export "use server" sem validação na primeira instrução.
2. O Pacote server-only
Importe server-only no topo de qualquer arquivo com credenciais de DB ou lógica interna. O build quebra se um Client Component importar transitivamente. Cuidado com barrel files — eles quebram a fronteira silenciosamente.
3. CSRF Além dos Defaults do Framework
O CVE-2026-27978 mostrou que o check Origin vs Host do Next.js tem edge cases (bypass de Origin: null via iframes sandboxed). Seta SameSite=Strict nos cookies de sessão, adiciona tokens CSRF por sessão para operações críticas, e nunca coloca "null" em allowedOrigins.
4. Verifique Sua Versão do Patch
O fix do RCE chegou em React 19.0.1, 19.1.2, 19.2.1. Fixes de DoS precisam de 19.0.4+, 19.1.5+ ou 19.2.4+. Checa teu lockfile:
npm ls react react-dom react-server-dom-webpack
5. Taint API — Guardrail de Dev, Não Fronteira de Segurança
taintObjectReference pega erros honestos (passar user object inteiro pro Client Component). Mas taint rastreia referências, não conteúdo — qualquer spread ou round-trip de serialização quebra. Defesa em profundidade útil, não fronteira primária.
6. WAFs — Redução de Ruído, Não Fronteira
WAFs pegam scanners automatizados. Não param atacantes motivados: buffers de inspeção são ~128KB, então prepende 130KB de padding e o payload passa. Trate WAF como redução de ruído, não segurança.
O Que Ainda Está Estruturalmente Exposto
- MITM no Flight stream — texto plano, estrutura previsível. Injeta imports
$I, server references$F, ou modifica data rowsD. - Enumeração de Server Actions —
server-reference-manifest.jsonexposto entrega um mapa completo de API para ataques IDOR. - Tampering de closures criptografadas —
NEXT_SERVER_ACTIONS_ENCRYPTION_KEYestático em multi-server + file read = closures forjadas. - Ativação de supply chain — pacotes npm dormentes bundled mas nunca carregados podem ser ativados via referências
$Iinjetadas.

Pra Onde Isso Vai
O Flight protocol resolve um problema genuinamente difícil: streaming de árvores de componentes interativas com hidratação progressiva e code splitting server-driven. Funciona. Mas funciona serializando referências executáveis num protocolo de texto e confiando na estrutura do stream dos dois lados.
O patch de hasOwnProperty está correto. Os fixes de DoS estão no lugar. Mas expor property traversal arbitrário e reconstrução de Thenables executáveis através de um protocolo de rede foi um erro de design. O patch trata o sintoma.
Esperar que o parser trate todo edge case não funcionou historicamente — GWT, JSF ViewState, ASP.NET ViewState seguiram o mesmo padrão. Se você shipa Server Components, leia react-client/src/ReactFlightClient.js. Saiba o que seu framework confia em seu nome.
Leitura complementar:
- Cloudflare DDoS Report H1 2026: Ataques de 1 Tbps Crescem 519% — O Que Isso Significa Para Sua Infraestrutura
- De Oracle para PostgreSQL no Azure: Um Blueprint Prático de Migração Enterprise
Fonte: Smashing Magazine — Weaponizing and Defending the React Flight Protocol