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

> Uma rota de autoatendimento aceitando o campo de papel do usuário foi tudo que faltou para uma escalada de privilégio. O caso, a correção e a regra que ficou.

Publicado em 2026-07-21 por Time Orion · Blog da Orion
Versão canônica: https://orion.moe/blog/input-false-escalada-de-privilegio
Índice do blog: https://orion.moe/blog

---

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.
