O Convite Que Criou um Proprietário: Lendo o Que o Formulário Envia
O desafio
A Vantage encontrou no seu workspace uma conta Proprietário que ninguém lembra de ter criado. A conta é de um colega real, convidado em um lote comum de quatro pessoas, e o líder de equipe que enviou o lote tem certeza de nunca ter mexido em papel nenhum. Ele está dizendo a verdade: a tela de convite não oferece controle de papel algum, e diz com todas as letras que quem for convidado por ali entra como Membro. Mas o que uma página mostra e o que uma página envia são dois documentos diferentes, e o servidor acreditou no segundo. Esta é a tela exatamente como foi submetida, capturada pela ferramenta de suporte. Leia a marcação e diga qual endereço chegou como Proprietário.
O que você vai aprender
- Comparar o que uma página renderiza com o que seu formulário enviaria
- Ler dois arrays posicionais como dados coindexados
- Explicar por que um input oculto não é nem secreto nem somente leitura
- Reconhecer a autorização fornecida pelo cliente como a falha subjacente
- Descrever por que esse escalonamento deixa um rastro de auditoria com aparência limpa
Habilidades testadas
Pré-requisitos
- O que são um formulário HTML e um input oculto
- Leitura de uma lista separada por vírgulas
Como funciona
Todo formulário é dois documentos. Há aquele que o usuário lê, e há o conjunto de pares de nome e valor que o navegador vai enviar. Eles deveriam concordar. Quando não concordam, o servidor quase sempre acredita no segundo, porque o segundo é o único que ele realmente vê.
A pré-visualização aqui é internamente consistente: quatro convidados, a palavra Membro ao lado de cada um, e uma frase prometendo que essa tela não concede mais nada. Não há controle de papel algum para abusar, e é exatamente por isso que o relato do líder de equipe é verdadeiro e mesmo assim está errado.
Ver código-fonte traz cinco campos que a página nunca desenhou. Dois deles são listas. invitees são quatro endereços em ordem e roles é member,member,owner,member. O comentário acima deles declara o contrato: roles[] é posicional e precisa ficar em sincronia com invitees[]. Alinhe-os e a terceira entrada de cada um emparelha [email protected] com owner.
A falha não é que um campo estava oculto. display:none e type="hidden" são instruções de layout, não controle de acesso. O valor ainda trafega pela rede, e qualquer um com ferramentas de desenvolvedor, um proxy de interceptação ou um único comando curl pode alterá-lo. A falha é que o servidor aceitou um papel vindo do cliente, então a promessa impressa na página era aplicada em exatamente um lugar: a página.
Duas coisas tornam esse padrão pior do que parece. Arrays paralelos ligados por índice são frágeis, e uma única reordenação em qualquer ponto da pilha reatribui silenciosamente todos os papéis. E o rastro depois é invisível: o convite, o aceite e o novo Proprietário parecem legítimos no log de auditoria, porque do ponto de vista do servidor um usuário autorizado pediu exatamente isso.
Erros comuns
- Responder [email protected]. A entrada
owneré a terceira, não a quarta. Conte as posições em vez de presumir que é a última. - Responder com o papel em vez do endereço. A pergunta pede quem, e o rótulo da resposta diz isso.
- Confiar nas linhas renderizadas. As quatro dizem Membro, e é por isso que a página visível não é prova.
- Chamar o atributo oculto de vulnerabilidade. Ocultar um campo não muda nada sobre se o navegador vai enviá-lo.
- Presumir que uma conta privilegiada fez isso. Um papel fornecido pelo cliente não precisa de privilégio algum, apenas de uma requisição.
Como se proteger
Decida os papéis no servidor, a partir de quem está pedindo e não do que foi enviado.
- Remova
rolesda requisição por completo. Se a tela só pode criar Membros, o handler deve atribuir Membro e ignorar qualquer papel presente no corpo. - Onde um papel realmente pode ser escolhido, verifique novamente no servidor se quem está chamando tem permissão para concedê-lo, e se o papel está no conjunto permitido para aquele plano e organização.
- Vincule a requisição à organização a partir da sessão, não de um campo
orgcontrolado pelo cliente. - Substitua os arrays paralelos por uma lista de objetos, um endereço e um papel por entrada, para que nada dependa de posição.
- Alerte sobre qualquer concessão de Proprietário que não tenha vindo da tela de gerenciamento de papéis. Esse escalonamento é silencioso justamente porque cada registro individual parece normal.