Revisão de Código Invertida: Absolvendo o Único Handler Realmente Seguro
O desafio
Quatro handlers do mesmo serviço de contas, escritos no mesmo estilo pela mesma equipe, todos decorados com login_required e todos lendo entrada da requisição. Três deles contêm uma vulnerabilidade real e diferente: um é injetável, um reflete marcação controlada pelo atacante, e um vai buscar qualquer endereço que você indicar. O quarto faz tudo certo. Este é o inverso do exercício habitual, então saber nomear um bug não basta: você precisa inocentar um handler, o que significa provar que os outros três estão errados. Envie o nome da única função que é segura.
O que você vai aprender
- Revisar código por eliminação, em vez de procurar um único sink óbvio
- Reconhecer SQL injection construído por interpolação de f-string
- Reconhecer XSS refletido a partir de concatenação sem escape numa resposta HTML
- Reconhecer SSRF por trás de uma checagem de URL que valida só o esquema
- Explicar por que login_required é autenticação, e não autorização
- Identificar as propriedades que realmente tornam um handler seguro
Habilidades testadas
Pré-requisitos
- Ler Python e entender o roteamento de um framework web
- Familiaridade com SQL injection, XSS e SSRF
Como funciona
A maioria dos exercícios de revisão de código pede para você achar o bug, o que recompensa o pattern matching: procurar uma função perigosa, apontá-la, seguir em frente. A revisão de verdade tem o formato oposto. Você precisa dar conta de todos os caminhos antes de poder aprovar qualquer coisa, e um handler só está limpo depois que você explica por que cada uma de suas entradas não pode te prejudicar.
Estes quatro handlers são deliberadamente uniformes. Vêm do mesmo arquivo, compartilham um estilo, todos carregam login_required, e todos leem entrada influenciada pelo atacante. O decorador é a armadilha: ele prova que quem chama está autenticado e nada além disso. Ele não parametriza uma consulta, não escapa uma resposta, nem valida um destino.
get_order interpola order_id no SQL com uma f-string. A rota o captura como string, então um payload chega intacto à instrução e pode encerrar a condição, o que quebra tanto a consulta quanto a verificação de posse de user_id que está na mesma string. render_receipt concatena o parâmetro note numa marcação retornada como text/html, o que é XSS refletido. fetch_logo exige que a URL comece com https:// e então a busca a partir do servidor, o que permite endereços de metadados link-local e nomes de serviços internos, o clássico desvio de SSRF.
update_contact é o único que sobrevive, e sobrevive por três propriedades específicas: entrada validada com uma expressão regular de correspondência completa e um limite de tamanho antes do uso, uma instrução parametrizada para que o valor nunca vire texto da instrução, e uma cláusula WHERE baseada em session['user_id'], de modo que a linha modificada é escolhida pelo servidor, não por quem chama.
Erros comuns
- Responder get_order porque ele verifica user_id. Essa verificação está dentro da mesma f-string injetável, então um payload pode neutralizá-la. Uma cláusula de posse que o atacante pode editar não é uma cláusula de posse.
- Responder fetch_logo porque ele valida a URL. Verificar o esquema não é validar o destino.
https://169.254.169.254/passa nesse teste. - Responder render_receipt porque order_id usa o conversor int. O identificador está correto; o parâmetro
noteé o que chega sem escape na resposta HTML. - Tratar login_required como o fator decisivo. Os quatro handlers o têm, então ele não consegue diferenciá-los.
- Parar na primeira vulnerabilidade encontrada. A pergunta é qual handler está limpo, então os quatro precisam ser revisados.
Como se proteger
Cada um dos três handlers com falhas tem uma correção bem estabelecida, e update_contact já demonstra o padrão que os outros deveriam seguir.
- Injeção. Sempre use parâmetros vinculados. Nunca construa SQL com f-strings ou concatenação, e restrinja as capturas de rota com o conversor correto, por exemplo
<int:order_id>. - Cross-site scripting. Renderize através de um template engine com auto-escape contextual em vez de concatenar strings, e adicione uma Content-Security-Policy para que um escape esquecido não seja imediatamente explorável.
- SSRF. Resolva o hostname e rejeite faixas privadas, loopback e link-local, reverifique após redirecionamentos, use uma allowlist de hosts permitidos, e roteie as buscas de saída por um proxy de egress.
- Autorização. Delimite toda consulta pelo principal da sessão, como
update_contactfaz, de modo que a posse seja imposta pelo servidor em vez de afirmada pela requisição. - Processo. Adicione linting para SQL construído por strings e respostas construídas sem escape, para que esses padrões falhem no CI em vez de na revisão.