Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Prototype Pollution no Node.js

Atualizado em: 8 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Prototype Pollution acontece quando propriedades controladas pelo usuário alteram protótipos de objetos JavaScript. Chaves como __proto__, constructor e prototype podem contaminar objetos criados posteriormente, modificar decisões de autorização, causar falhas ou preparar outros ataques.

Exemplo vulnerável

function merge(target, source) {
  for (const key in source) {
    if (typeof source[key] === 'object') {
      target[key] ||= {};
      merge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}

Um payload com caminhos perigosos pode alcançar o protótipo global, dependendo da implementação.

Impacto

Exemplos:

  • isAdmin aparece como true em objetos sem campo próprio;
  • configurações recebem valores inesperados;
  • loops e serialização mudam;
  • bibliotecas usam opções contaminadas;
  • negação de serviço;
  • cadeias com template injection ou execução.

Valide propriedades próprias

if (Object.hasOwn(user, 'isAdmin') && user.isAdmin === true) {
  // decisão explícita
}

Não confie em uma propriedade apenas porque a leitura devolveu valor.

Bloqueie chaves perigosas

const BLOCKED_KEYS = new Set([
  '__proto__',
  'prototype',
  'constructor',
]);

function assertSafeKey(key) {
  if (BLOCKED_KEYS.has(key)) {
    throw new ValidationError('Chave não permitida');
  }
}

Aplique recursivamente em estruturas dinâmicas, mas prefira schemas com propriedades conhecidas.

additionalProperties false

JSON Schema pode rejeitar campos inesperados:

{
  "type": "object",
  "properties": {
    "theme": { "enum": ["light", "dark"] }
  },
  "additionalProperties": false
}

Isso reduz objetos arbitrários chegando a funções de merge.

Object.create(null)

const dictionary = Object.create(null);
dictionary[userKey] = value;

Objetos sem protótipo são úteis como dicionários. Ainda limite chaves e tamanho.

Map

Para chaves arbitrárias, Map evita semântica de protótipo:

const values = new Map();
values.set(userKey, value);

Spread e Object.assign

Spreads modernos possuem comportamento diferente de merges recursivos, mas não transforme isso em garantia universal. O risco depende de setters, destinos e bibliotecas. Use allowlist de campos:

const preferences = {
  theme: input.theme,
  language: input.language,
};

Evite merge profundo de entrada

Não mescle body do usuário diretamente em configuração, entidade ou opções de biblioteca:

// Evite
const config = deepMerge(defaultConfig, req.body);

// Prefira
const config = {
  timeoutMs: validateTimeout(req.body.timeoutMs),
  mode: validateMode(req.body.mode),
};

Bibliotecas

Funções antigas de merge, set por caminho e query parser já tiveram vulnerabilidades. Mantenha dependências atualizadas, use lockfile e revise advisories.

Query strings

Parsers que transformam a[b][c] em objetos profundos precisam de limites e proteção. Configure profundidade, quantidade de parâmetros e chaves proibidas.

JSON.parse

O parse por si só cria propriedades próprias, mas o risco aparece quando o resultado é mesclado ou interpretado por código inseguro. Não considere JSON.parse uma sanitização.

Configurações de segurança

Nunca use defaults herdáveis para autorização:

const allowed = Object.hasOwn(decision, 'allowed') &&
  decision.allowed === true;

Freeze

Object.freeze protege um objeto específico contra mudanças superficiais, não elimina poluição global nem congela estruturas internas. Pode complementar configurações imutáveis.

Node flags

Versões do Node.js oferecem opções relacionadas ao tratamento de __proto__. Elas podem reduzir superfície, mas não substituem validação e atualização de bibliotecas.

Testes

const payload = JSON.parse('{"__proto__":{"polluted":true}}');
expect(() => updatePreferences(payload)).toThrow();
expect({}.polluted).toBeUndefined();

Teste constructor.prototype, caminhos aninhados, query strings e bibliotecas de merge.

Detecção

Secret scanners não detectam esse problema. Use SAST, auditoria de dependências, testes adversariais e revisão de funções que aceitam caminhos ou objetos dinâmicos.

Observabilidade

Registre rejeições por chave categorizada, sem copiar payload completo. Um pico pode indicar scanner automatizado.

Erros comuns

  • merge profundo de req.body;
  • confiar em propriedades herdadas;
  • permitir chaves arbitrárias;
  • parser de query sem limite;
  • dependência desatualizada;
  • considerar JSON.parse seguro por si só;
  • usar objeto comum como dicionário;
  • tentar corrigir apenas removendo __proto__ no nível superior.

Fluxo recomendado

  1. valide com schema;
  2. use allowlist de campos;
  3. bloqueie chaves perigosas recursivamente;
  4. evite merge profundo;
  5. use Map ou objeto sem protótipo;
  6. verifique propriedades próprias;
  7. atualize dependências;
  8. teste payloads adversariais;
  9. monitore rejeições.

Combine a proteção com JSON Schema, Tratamento de Erros, Rate Limiting e Audit Logs.

Consulte a referência da OWASP sobre Prototype Pollution e a documentação de protótipos da MDN.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita