Pular para o conteúdo
>_ITDITDPlataforma de Segurança Web

Guias de Segurança

Prototype pollution no Node.js: o que o runtime não cobre e como se defender no seu próprio código

Uma chave especial nos dados recebidos, levada por um merge recursivo, faz propriedades que você nunca definiu aparecerem em objetos sem relação. O Node.js diz que isso não é uma vulnerabilidade do núcleo, então a defesa é responsabilidade sua.

Publicado 2026-09-05 Atualizado 2026-10-04 Última verificação 2026-09-05 8 min de leitura

Você recebe dados de fora e, depois, propriedades que você nunca definiu começam a aparecer em objetos sem relação. Ou um método padrão como hasOwnProperty de repente "não é uma função" e o app cai. As duas coisas são sintomas de prototype pollution (poluição de protótipo).

O que o Node.js cobre e o que fica com você

O Node.js diz claramente que esta vulnerabilidade não é dele

O guia oficial de segurança do Node.js é explícito: "Segundo o modelo de ameaças do Node.js, a prototype pollution que depende de um atacante controlar a entrada do usuário não é considerada uma vulnerabilidade do núcleo do Node.js, porque o Node.js confia nas entradas fornecidas pelo código da aplicação."

E logo em seguida: "Mesmo assim, a prototype pollution é uma classe séria de vulnerabilidades para aplicações Node.js e bibliotecas de terceiros, e você deve implementar defesas no nível da aplicação e das dependências."

Isso não é uma contradição; é a declaração de onde fica a responsabilidade. O runtime se coloca do lado que não garante que a entrada esteja bem formada. Ou seja, "esperar um CVE, esperar uma atualização" não funciona aqui. É mais um caso de algo com que este site esbarra o tempo todo: a parte que você supunha que alguém cuidava não tem ninguém responsável por ela.

O que dá errado (dois resultados)

Entrada externa (JSON, query, formulário)

traz uma chave especial

↓ merge recursivo / cópia profunda / montagem de configuração

O modelo compartilhado (protótipo) é reescrito

↓ e com ele tudo o que herda dele

1. Aparecem valores que você nunca definiu

flags de permissão e verificações de opções se desviam

2. Métodos nativos desaparecem

o app cai num ponto sem relação (DoS)

Uma gravação feita para um objeto afeta todos que compartilham o modelo. Por isso o dano aparece num ponto sem relação.
Os dois resultados, em termos operacionais
1. Injeção de propriedades
Um valor "que não poderia ter sido definido" passa a ser lido. Em qualquer lugar onde um objeto de opções ou uma flag de permissão é decidido por uma simples verificação de existência, o valor injetado é simplesmente aceito. Se chegar a uma decisão de autorização, vira um problema de privilégios; se chegar à montagem de templates ou comandos, pode ficar bem pior
2. Negação de serviço
Quebre o próprio modelo e um método nativo como hasOwnProperty gera um erro "is not a function". A documentação apresenta isso como um possível DoS. O ponto que vale guardar: não se trata só de escalada de privilégios
Até onde chega
Os exemplos citados são o próprio Node.js (CVE-2022-21824) e uma biblioteca de terceiros (Lodash, CVE-2018-3721). Não escrever um merge recursivo você mesmo não muda nada se uma dependência escreveu um

As mitigações que a documentação lista (sete)

O Node.js as enumera. Fica mais fácil aplicá-las agrupando pelo que fazem: barrar na entrada, tornar a estrutura impossível de poluir e mudar a forma de ler.

Barrar na entrada

・Evitar merges recursivos inseguros (a documentação cita o CVE-2018-16487)
・Implementar validações com JSON Schema para requisições externas e não confiáveis

Nunca deixar entrar uma chave inesperada é o caminho mais seguro. Reavalie designs que aceitam um objeto profundo inteiro e o mesclam ao estado existente.

Mudar a estrutura e a leitura

・Object.create(null) para criar objetos sem protótipo (ideal para dicionários)
・Object.freeze(MyObject.prototype) para congelar o protótipo
・A flag --disable-proto do Node para desativar Object.prototype.proto
・Object.hasOwn(obj, key) para verificar se a propriedade é do próprio objeto
・Evitar usar métodos de Object.prototype

A mudança de maior efeito é a forma de testar a existência

Na prática, o ganho mais amplo é passar a usar Object.hasOwn(obj, key). Uma verificação de existência simples — essa propriedade existe? — também responde sim para valores herdados do modelo, que é exatamente como um valor injetado é captado. Confirmar que a propriedade é do próprio objeto evita sozinho a maior parte do resultado 1.

Pelo mesmo motivo, evite chamar obj.hasOwnProperty(key) a partir do próprio objeto — esse método é uma das coisas que a poluição pode remover (resultado 2).

O que verificar no seu próprio código

1

Você aceita entrada externa como um objeto profundo?

Corpos de requisição, query strings, formulários, payloads de webhook — encontre todos os lugares que recebem uma estrutura aninhada inteira e a mesclam ao estado existente. É só por ali que a entrada externa entra. Decidir explicitamente quais chaves você aceita, por meio de um schema, é a correção mais curta.

2

Mude a forma de criar objetos do tipo dicionário

Tudo o que é usado como "um contêiner cujas chaves são decididas em tempo de execução" (um mapa indexado por ID, um conjunto de configurações) deve ser criado com Object.create(null). Sem modelo, não há nada para herdar. Resolva na estrutura o que de outro modo você teria de resolver na validação.

3

Padronize as verificações de existência com Object.hasOwn

Procure ramificações que dependem de uma propriedade existir e converta-as. Verificações de permissão, flags e opções vêm primeiro — elas se ligam diretamente à forma como a autorização é implementada.

4

Coloque uma máquina para vigiar as dependências

Não escrever um merge recursivo você mesmo não muda nada se uma dependência escreveu um — e é por isso que os exemplos citados incluem uma biblioteca de terceiros. Vulnerabilidades conhecidas em dependências não podem ser acompanhadas à mão, então coloque-as sob monitoramento automático como em primeiros passos com o osv-scanner. O risco mais amplo de uma dependência envenenada está em como se defender do comprometimento da cadeia de suprimentos do npm.

5

Olhe da mesma forma para a entrada do seu framework de servidor

Todo framework Node tem um caminho que desserializa o corpo da requisição. Quando verificamos os dados do NVD, as vulnerabilidades do Next.js se concentravam em responsabilidades do lado do servidor — cache, middleware, Server Actions (Next.js e React têm muitas vulnerabilidades?). Comece abandonando a suposição de que "é uma biblioteca de front-end, então isso não se aplica".

A visão deste site: onde a responsabilidade é declarada explicitamente, verifique você mesmo

O mais útil na prática neste artigo não é a lista de mitigações — é a frase dizendo que isso não é considerado uma vulnerabilidade do núcleo do Node.js. Onde a responsabilidade é declarada com tanta clareza, ninguém cuida do que fica fora dela: nenhum CVE vai chegar e nenhuma atualização vai corrigir.

Tratamos isso como o mesmo tipo de problema de um certificado que não protege você de uma tomada de controle (subdomain takeover) e de um bloqueio que não apaga a política existente por baixo dele (buckets públicos). Quem é de fato responsável pelo lugar que você supunha estar coberto? Responder a isso uma vez costuma reordenar bastante as suas prioridades.

Fontes (primárias)

  • Node.js, "Security Best Practices" (seção Prototype Pollution Attacks / CWE-1321) — nodejs.org (a posição do modelo de ameaças, os dois resultados, os CVEs citados e as sete mitigações vêm todos deste documento; consultado em 5 de setembro de 2026)

Leia a seguir

FAQ

QPrototype pollution é um bug do Node.js? Uma atualização vai corrigir?
A

Nenhuma atualização vai chegar. O guia oficial de segurança do Node.js declara que, segundo o modelo de ameaças do Node.js, a prototype pollution que depende de um atacante controlar a entrada do usuário não é considerada uma vulnerabilidade do núcleo do Node.js, porque o Node.js confia nas entradas fornecidas pelo código da aplicação. E continua: mesmo assim, a prototype pollution é uma classe séria de vulnerabilidades para aplicações Node.js e bibliotecas de terceiros, e você deve implementar defesas no nível da aplicação e das dependências. Por design, isso é responsabilidade sua.

QO que de fato dá errado?
A

Dois resultados. Primeiro, propriedades que você nunca definiu começam a aparecer em objetos de todo o processo — se uma flag de permissão ou uma opção é decidida por uma simples verificação de existência, esse valor injetado é aceito, e a autorização ou a lógica de negócio se desviam. Segundo, negação de serviço: quebre o protótipo nativo e um método padrão como hasOwnProperty gera um erro 'is not a function', derrubando código que não tem nada a ver com a entrada. O segundo resultado mostra que não se trata só de escalada de privilégios.

QOnde eu corrijo?
A

Na entrada e nas estruturas de dados. Na entrada, aplique validação com JSON Schema às requisições externas e não confiáveis para que chaves inesperadas nunca entrem. Nas estruturas, pare de usar merges recursivos inseguros e crie objetos do tipo dicionário com Object.create(null) para que não tenham protótipo algum. Na leitura, use Object.hasOwn(obj, key) para confirmar que a propriedade é do próprio objeto, e evite depender de métodos de Object.prototype. Na operação, há ainda Object.freeze e a flag --disable-proto do Node.

QNunca escrevi um merge recursivo — estou seguro?
A

Não. Os exemplos citados pela documentação são tanto o próprio Node.js (CVE-2022-21824) quanto uma biblioteca de terceiros (Lodash, CVE-2018-3721). Mesclagem de configurações, parsing de query string e desserialização de formulários são todos lugares onde bibliotecas muito usadas montam objetos profundos por você. Por isso a defesa não se fecha dentro do seu código; ela vem junto com o monitoramento automático das suas dependências.