FlawOpen

flawopen.com/Poluição de protótipo/JSON.parse()

JSON.parse() causa poluição de protótipo?

CWE-1321: Poluição de protótipoGuia de referência
Resposta curta

Não. JSON.parse() cria uma propriedade própria chamada __proto__; a análise não chama o setter herdado nem altera um protótipo. O risco começa quando outro código copia essa chave por um setter, mescla recursivamente chaves não confiáveis ou grava em um caminho de objeto controlado pelo atacante.

CÓPIA INSEGURA — ALVO LOCAL
// JSON.parse cria dados; não polui um protótipo.
const input = '{"__proto__":{"isAdmin":true}}';
const parsed = JSON.parse(input);

// Object.assign usa Set no destino. Aqui altera apenas
// o protótipo deste objeto local de demonstração.
const copied = Object.assign({}, parsed);

Object.getPrototypeOf(copied).isAdmin; // true
Object.prototype.isAdmin;              // undefined
TRATAMENTO SEGURO — FORMA CONHECIDA
// Analise e construa apenas os campos aceitos pela aplicação.
const input = '{"theme":"dark","locale":"en","__proto__":{"isAdmin":true}}';
const parsed = JSON.parse(input);
const config = {
  theme:  parsed.theme,
  locale: parsed.locale,
};

// Para mapas com chaves não confiáveis, evite setters herdados.
const options = Object.assign(Object.create(null), parsed);

Por que JSON.parse() não é o ponto de poluição

Uma chave JSON e o setter de protótipo de um literal de objeto são operações diferentes. A análise JSON define propriedades próprias no novo valor; ela não executa obj.__proto__ = value. Operações posteriores podem se comportar de outra forma: Object.assign(target, source) usa atribuição no destino, então uma chave própria __proto__ pode chamar o setter herdado do destino. Isso altera o protótipo do objeto de destino; por si só, não modifica Object.prototype.

Depois de const o = JSON.parse('{"__proto__":{"a":1}}'), Object.hasOwn(o, "__proto__") é verdadeiro, enquanto Object.prototype.a continua indefinido. Se uma cópia posterior atribuir essa chave a um alvo normal, esse alvo pode receber outro protótipo. O spread de objetos cria propriedades próprias em vez de chamar o setter, mas não protege código de mesclagem recursiva ou definição de caminhos insegura.

Onde aparece o risco real

JSON analisado é dado não confiável, não um protótipo poluído. O perigo depende dos pontos de escrita seguintes: uma mesclagem recursiva, um setter de caminho ou um laço de cópia que atribui propriedades a um objeto comum. Uma mesclagem recursiva vulnerável pode seguir __proto__ ou constructor.prototype até um protótipo compartilhado; uma cópia de um nível pode alterar somente o alvo. Verifique a função e o objeto de destino antes de descrever o impacto.

Revise onde as chaves não confiáveis são gravadas, não apenas onde o JSON é analisado: funções de mesclagem profunda, setters de caminho e laços for...in que fazem atribuições. Rejeite __proto__, constructor e prototype quando as chaves puderem ser controladas por usuários; prefira validar um formato conhecido ou usar um dicionário sem protótipo.

Perguntas frequentes

Devo sempre usar um reviver para remover __proto__?

Um reviver pode remover essa chave durante a análise, o que é útil se a aplicação não precisa dela. É defesa em profundidade, não substitui corrigir pontos inseguros de mesclagem e definição de caminhos; constructor.prototype e fontes que não sejam JSON também podem importar.

Express ou body-parser me protegem?

O parser do corpo produz um valor JavaScript; ele não torna seguras as cópias ou mesclagens posteriores. Revise o parser exato e as operações seguintes. Alguns parsers de query string também constroem objetos aninhados com notação de colchetes, uma via de entrada separada.

JSON.stringify() causa risco de poluição de protótipo?

Não. A serialização lê valores e produz texto JSON; ela não altera o protótipo de um objeto. O risco pode reaparecer quando esse texto é analisado e enviado a um ponto inseguro.

Referências

Veja também Poluição de protótipos JavaScript: causas e correções JSON.parse() é mais seguro que eval()? lodash.merge() é seguro contra poluição de protótipo? Object.assign() e Prototype Pollution: o que é seguro?