flawopen.com/Prototype Pollution/Object.assign()
Object.assign() é seguro contra Prototype Pollution?
Não completamente. Object.assign() copia propriedades próprias enumeráveis atribuindo-as ao alvo. Se a origem tiver uma chave própria enumerável __proto__, a atribuição pode chamar o setter herdado e alterar o protótipo do objeto de destino. Um assign superficial não modifica, por si só, o Object.prototype compartilhado; um merge recursivo pode causar um impacto mais amplo.
// A JSON own "__proto__" key reaches the inherited setter const source = JSON.parse('{"__proto__":{"isAdmin":true}}'); const target = Object.assign({}, source); // target's prototype changes; Object.prototype does not. // Recursive merge can reach Object.prototype function deepAssign(target, source) { for (const key of Object.keys(source)) { if (source[key] && typeof source[key] === 'object') { target[key] = target[key] || {}; deepAssign(target[key], source[key]); // With key === '__proto__', target[key] can be // Object.prototype; recursion then writes into it. } else { Object.assign(target, { [key]: source[key] }); } } }
// Spread creates an own data property; it does not invoke the setter. const shallow = { ...userInput }; // Reject dangerous keys at every depth and use own data properties. const blocked = new Set(['__proto__', 'constructor', 'prototype']); function isRecord(value) { if (value === null || typeof value !== 'object' || Array.isArray(value)) return false; const proto = Object.getPrototypeOf(value); return proto === Object.prototype || proto === null; } function copySafe(value) { if (Array.isArray(value)) return value.map(copySafe); return isRecord(value) ? mergeSafe(value) : value; } function mergeSafe(source, target = Object.create(null)) { if (!isRecord(source)) throw new TypeError('Expected a plain object'); for (const key of Object.keys(source)) { if (blocked.has(key)) continue; const value = source[key]; const current = Object.hasOwn(target, key) ? target[key] : undefined; const next = isRecord(value) ? mergeSafe(value, isRecord(current) ? current : Object.create(null)) : copySafe(value); Object.defineProperty(target, key, { value: next, enumerable: true, writable: true, configurable: true }); } return target; }
O que Object.assign() faz com a chave __proto__
Object.assign() copia cada propriedade própria enumerável da origem usando o mecanismo normal de definição de propriedades do alvo. Se uma origem analisada de JSON tiver uma propriedade própria __proto__ e o alvo herdar o setter legado, esse setter altera o [[Prototype]] do alvo. Nesse caso, não é criada uma propriedade própria literal chamada __proto__.
O spread de objeto ({ ...source }) cria propriedades de dados no resultado; a mesma chave vira uma propriedade própria e não chama o setter herdado. Essa diferença importa em cópias superficiais, mas não protege merges recursivos posteriores.
Como merges recursivos podem poluir protótipos compartilhados
Prototype Pollution ocorre quando código recursivo inseguro segue chaves controladas pelo atacante e escreve em um protótipo compartilhado. Um padrão comum lê target[key] para localizar um objeto aninhado. Se key for __proto__, a leitura pode retornar Object.prototype; a recursão seguinte pode então afetar objetos comuns globalmente. Caminhos como constructor.prototype também exigem atenção.
Na revisão, procure leituras e escritas no alvo com chaves controladas pelo atacante, verifique se invocam setters e confirme que cada nível recursivo rejeita chaves perigosas.
Perguntas frequentes
{ ...obj }) é igualmente seguro?Sim, para uma cópia superficial: o spread cria uma propriedade própria de dados e não chama o setter herdado. Já Object.assign() pode alterar o protótipo do alvo ao receber uma chave enumerável __proto__. Nenhuma dessas operações superficiais, sozinha, modifica o Object.prototype compartilhado.
structuredClone() produz uma cópia separada sem aplicar a cadeia de protótipos da origem. Uma chave própria __proto__ continua sendo dado na cópia; um merge inseguro posterior ainda pode interpretá-la de forma especial.
Use uma biblioteca de merge mantida e atualizada; confira como ela trata __proto__, constructor e prototype em todos os níveis. Utilitários populares já tiveram avisos de Prototype Pollution; somente versões corrigidas incluem a proteção.