O redirecionamento que só foi meio validado: roubando um code do OAuth
O desafio
O Loomhaus faz o login com OAuth. Ao montar o link de login, ele verifica se o redirect_uri que você pediu pertence ao app, e essa verificação consiste em checar se o valor começa com o endereço registrado https://app.loomhaus.example. O servidor de autorização vai enviar o código de uso único para o redirect_uri que sobreviver a essa verificação. Uma vítima já clicou em um link que você preparou; o endpoint de log do atacante registrou o que chegou. Monte o redirect_uri que passa na verificação mas entrega o código ao atacante, e então leia o código capturado.
O que você vai aprender
- Explicar por que hostnames são lidos da direita para a esquerda e por que uma comparação de prefixo não é uma verificação de origem
- Montar um redirect_uri que passa no startsWith, mas aponta para um host do atacante
- Descrever como um authorization code vazado leva ao sequestro de conta
- Justificar por que allowlists de correspondência exata são a única validação segura de redirect_uri
- Explicar como o PKCE torna um code roubado impossível de resgatar
Habilidades testadas
Pré-requisitos
- A etapa do authorization code do OAuth em alto nível
- Como uma URL é estruturada
Como funciona
No fluxo authorization code, o servidor envia um code de uso único para o redirect_uri que o cliente pediu, então validar essa URI é toda a segurança da etapa. Se um atacante consegue fazer o code ser entregue em um host que ele controla, ele pode concluir o login como a vítima.
Uma checagem de começa com o nosso endereço? parece razoável e não é. https://app.loomhaus.example é o prefixo literal de https://app.loomhaus.example.evil-capture.example, onde o nome real do app virou os rótulos mais à esquerda de um domínio que o atacante registrou. Hostnames são comparados da direita para a esquerda. Um teste de string ancorado à esquerda ignora isso por completo.
A única validação sólida é a correspondência exata com URIs de redirecionamento totalmente qualificadas e previamente registradas. startsWith, endsWith e contains falham, cada um, diante de um host com formato diferente.
Erros comuns
- Usar um host de atacante puro.
https://evil-capture.example/cbnão começa com o endereço registrado, então a checagem o rejeita. - Remover o cookie da vítima. O endpoint authorize só emite um code para uma sessão autenticada, e a requisição já carrega uma.
- Responder com a URL de redirecionamento. O objetivo é o valor de
codeno log do atacante, não o location. - Achar que o code é inofensivo. Sem vínculo com PKCE, quem tem o code pode trocá-lo.
Como se proteger
Valide o destino do redirecionamento de forma exata e vincule o code ao cliente.
- Compare o
redirect_uricom uma allowlist de URIs exatas e totalmente qualificadas, registradas para o cliente. Nunca usestartsWith,endsWithou testes de substring. - Exija PKCE: o code só pode ser trocado por quem possui o verifier original, então um code roubado é inútil.
- Restrinja o escopo e reduza a validade dos codes, e gere alertas para requisições authorize cujo host de redirecionamento não seja o registrado.