Blog

Como qualquer usuário logado virava admin — e a linha que corrigiu

21 de julho de 2026 · Time Orion

A vulnerabilidade mais constrangedora que encontramos no nosso próprio código não tinha nada de sofisticada. Não exigia ferramenta, nem conhecimento de protocolo, nem sequer sair do navegador.

Bastava estar logado e digitar uma linha no console.

O caso

Usávamos uma biblioteca de autenticação que permite estender o usuário com campos extras. Adicionamos os nossos — entre eles o papel, que decide o que a pessoa pode fazer no painel.

A mesma biblioteca oferece uma rota de autoatendimento para o usuário atualizar o próprio perfil: nome, preferências, esse tipo de coisa. É uma rota legítima, exposta por padrão, e — este é o ponto — ela não verifica permissão, porque não precisa: a pessoa está mexendo em si mesma.

O problema é que ela atualiza os campos que estão declarados como aceitáveis na entrada. E o nosso campo de papel estava.

Resultado: qualquer usuário autenticado, do papel mais restrito, podia enviar uma requisição para a rota de atualização de perfil declarando-se administrador. E funcionava.

Reproduzimos, confirmamos e corrigimos no mesmo dia.

A correção

Uma propriedade na declaração do campo:

role: { type: "string", input: false }

input: false diz à biblioteca que aquele campo existe no usuário mas nunca é aceito vindo do cliente. O papel passa a ser gravável apenas pelo caminho administrativo do nosso próprio código, que checa permissão.

Uma linha. E a lição não é sobre a linha.

O que a gente estava fazendo de errado

Confundir "campo do usuário" com "campo editável pelo usuário". Assim que se adiciona um campo à entidade de autenticação, a pergunta que precisa ser feita, campo a campo, é: quem pode escrever nisso? Nome e avatar: o próprio. Papel, status de ativo, vínculo com organização, limite de crédito: nunca o próprio.

Herdar superfície sem inventariá-la. A rota estava lá desde a instalação, documentada, e ninguém tinha olhado o que ela aceitava depois que estendemos o modelo. Toda biblioteca de autenticação traz rotas de autoatendimento; a extensão do modelo muda o que essas rotas podem fazer, e essa mudança é invisível no diff que adiciona o campo.

Presumir que "não documentado como perigoso" significa seguro. O comportamento estava correto do ponto de vista da biblioteca. O contrato dela é: "eu atualizo o que você declarou como entrada". Fomos nós que declaramos errado.

A regra que ficou

Todo campo sensível de identidade nasce não gravável pelo cliente. Ele só muda por um caminho que verifica permissão explicitamente.

E o hábito que acompanha: quando estendemos uma biblioteca que expõe rotas próprias, listamos as rotas dela e testamos cada campo novo contra cada uma. É uma tarde de trabalho e evita a categoria inteira.

Por que isso é comum

Escalada de privilégio por atribuição em massa é uma das vulnerabilidades mais antigas que existem — a versão clássica é o formulário de cadastro que aceita um campo admin escondido. Ela sobrevive porque o mecanismo que a causa é o mesmo que dá conveniência: aceitar um objeto e aplicar seus campos.

Frameworks modernos mitigam com listas de permissão. E a mitigação falha exatamente no ponto em que o desenvolvedor estende o modelo e esquece de revisar a lista — que é o nosso caso.

O que fazer hoje, no seu sistema

Três perguntas que levam quinze minutos e podem valer muito:

  1. Que rotas de autoatendimento a minha biblioteca de auth expõe? (Atualizar perfil, trocar e-mail, atualizar metadados.)
  2. Que campos eu adicionei ao usuário depois de instalar?
  3. Algum desses campos decide permissão, cobrança ou vínculo?

Se a interseção entre 2 e 3 não estiver marcada como não-editável, você tem o mesmo bug.

O resumo

A vulnerabilidade não estava na biblioteca — estava na fronteira entre o que ela oferece e o que a gente acrescentou. Fronteira de biblioteca é onde mora o bug que ninguém revisa, porque cada lado parece correto isoladamente.

Corrigir custou uma linha. Encontrar custou uma pergunta que a gente devia ter feito no dia em que adicionou o campo.