A chave que envenenou a base: Prototype Pollution
O desafio
O endpoint de preferências da Thornfield recebe qualquer JSON que você enviar e o mescla às suas configurações com um pequeno helper recursivo. O helper copia toda chave que encontra no seu objeto, entrando nos objetos aninhados pelo caminho. Um nome de chave é especial em JavaScript, e escrever através dele não altera o seu objeto: altera aquilo de onde todo objeto herda. Leia o código e dê o nome da chave que um atacante envia para definir uma propriedade em todos os objetos do processo de uma só vez.
O que você vai aprender
- Explicar como __proto__ alcança o prototype compartilhado de um objeto
- Ver como um merge recursivo sem proteção escreve no prototype
- Rastrear como a leitura de uma propriedade com valor padrão (isAdmin) passa a ser true para todos os usuários
- Nomear __proto__, constructor e prototype como as chaves a recusar
- Descrever as defesas de merge seguro e de objetos com prototype nulo
Habilidades testadas
Pré-requisitos
- Leitura de JavaScript
- A ideia de que objetos herdam de um prototype
Como funciona
Todo objeto JavaScript comum herda de um prototype compartilhado, acessível pela chave __proto__. Ler uma propriedade que o objeto não define faz a busca cair nesse prototype.
Um merge recursivo que copia todas as chaves da entrada do atacante e desce em objetos aninhados vai seguir um __proto__ aninhado e atribuir no prototype compartilhado, em vez de no objeto de entrada. Um corpo como {"__proto__": {"isAdmin": true}} define, portanto, isAdmin no prototype do qual todo objeto herda.
Depois disso, qualquer objeto sem um isAdmin próprio resolve a busca como true. Uma verificação de autorização que lê essa propriedade com valor padrão passa a liberar todo mundo.
Erros comuns
- Responder
isAdmin. Essa é a propriedade que é poluída; a chave que o merge deixa de recusar é__proto__. - Responder
merge. A função é o veículo, mas a pergunta pede a chave. - Achar que só o seu objeto muda. Escrever através de
__proto__altera o prototype compartilhado, afetando todos os objetos. - Presumir que um padrão de ausente é seguro. A pollution faz propriedades ausentes resolverem para um valor do atacante.
Como se proteger
Recuse as chaves perigosas e evite leituras de segurança baseadas em valores padrão.
- Em qualquer merge ou deep-assign sobre dados não confiáveis, ignore ou rejeite
__proto__,constructoreprototype. - Use objetos com prototype nulo (
Object.create(null)) ou umMappara dados indexados por nomes não confiáveis. - Prefira uma biblioteca de merge auditada ou
structuredClonea um merge recursivo feito à mão; no Node, considere--disable-proto. - Não baseie uma decisão de segurança em uma propriedade que simplesmente se espera ausente por padrão.