- Castro/
- blog/
- Origin retorna 200, mas o usuário recebe 403: como descobrir em qual camada está o bloqueio/
Origin retorna 200, mas o usuário recebe 403: como descobrir em qual camada está o bloqueio
Como comparar origin, WAF e CDN quando o backend responde 200, mas a URL pública recebe um 403 de challenge na borda.
Uma URL pública começou a responder HTTP 403.
O primeiro impulso era olhar para a aplicação.
Só que a aplicação, quando testada diretamente no origin, respondia HTTP 200.
No mesmo ambiente, outra rota pública atrás de uma camada diferente também respondia 200.
A coleta externa ainda trazia uma pista forte:
| |
Em outra resposta apareciam headers típicos de um WAF intermediário.
Esse é um daqueles casos em que perguntar “o site está fora?” é pouco útil.
A pergunta melhor é:
em qual camada o status mudou de 200 para 403?
O caminho real tinha mais de uma camada #
A topologia, de forma sanitizada, era parecida com:
| |
Quando existem várias camadas HTTP, um único curl na URL pública mostra só o resultado final.
Ele não prova que o Nginx gerou aquele status.
Também não prova que o WAF foi o responsável.
Muito menos que a aplicação devolveu 403.
O teste que mudou a investigação: origin direto #
No caso real, a mesma rota foi consultada diretamente no origin.
A resposta foi:
| |
Enquanto a URL pública que passava pela borda devolvia:
| |
Essa comparação não resolve automaticamente a política que causou o bloqueio.
Mas elimina uma hipótese importante:
o backend não estava simplesmente devolvendo 403 naquela requisição direta.
A mudança de status acontecia em algum ponto entre origin e cliente.
Status code é uma evidência de camada #
Eu gosto de pensar a investigação como uma sequência de checkpoints.
| |
Quando o status muda, a investigação ganha um limite bem mais útil.
Se:
| |
não faz sentido começar alterando PHP, banco ou código da aplicação sem uma evidência adicional.
Como testar sem desmontar a arquitetura #
Quando o origin é administrado por você e o teste é autorizado, dá para comparar a resposta preservando Host e, no HTTPS, o SNI.
Para HTTP:
| |
Para HTTPS:
| |
Isso evita um erro comum: acessar o IP direto e receber o vhost default, concluindo alguma coisa sobre uma aplicação que nem foi atendida naquele request.
Depois, salvar a resposta pública inteira #
Eu prefiro olhar headers completos:
| |
E comparar com o origin:
| |
A diferença pode mostrar:
- status HTTP;
Server;- headers de challenge;
- headers de WAF;
- cookies de proteção;
- redirects;
- CSP;
- cache;
- headers adicionados/removidos.
O objetivo não é adivinhar a ferramenta pelo nome de um header.
É entender onde a resposta começou a mudar.
cf-mitigated: challenge era uma evidência bem específica #
Na resposta 403 do caso havia:
| |
Isso é muito mais informativo do que apenas:
| |
O primeiro indica que a resposta estava relacionada a um mecanismo de challenge/mitigação naquela borda.
O segundo, sozinho, só diz qual camada entregou a resposta ao cliente.
Essa distinção evita culpar o último hop por qualquer comportamento que ocorreu antes dele.
Outra rota respondia 200 através do WAF #
Na mesma coleta, uma URL pública que passava por outra composição de borda retornou:
| |
Isso não prova que “Sucuri funciona e Cloudflare não funciona”.
As rotas, políticas, hosts e regras podem ser diferentes.
Mas ajuda a confirmar que o problema não era uma indisponibilidade genérica do origin.
Havia comportamento HTTP diferente dependendo do caminho de publicação.
O erro mais fácil seria desligar a proteção para testar em produção #
Quando a borda entra como suspeita, uma tentação é remover proxy, desligar WAF ou mudar DNS de forma ampla.
Eu evitaria começar por aí.
Primeiro dá para coletar muita evidência de forma read-only:
| |
Se for necessário mudar uma regra, fica muito mais fácil limitar a alteração depois de saber qual request está sendo bloqueado e por quê.
403 de borda não é o mesmo que 403 da aplicação #
Uma aplicação pode responder 403 por autenticação, RBAC ou regra de negócio.
Nginx pode responder 403 por acesso a arquivo/diretório ou configuração.
Um WAF pode responder 403 por assinatura de ataque.
Uma CDN pode responder 403 por challenge, firewall rule, bot rule ou política de segurança.
Para o usuário final, tudo isso pode parecer:
| |
Para troubleshooting, são problemas completamente diferentes.
Um roteiro que funciona bem #
Quando o origin parece saudável e a URL pública falha:
1. Resolver DNS #
| |
2. Capturar a resposta pública #
| |
3. Testar o origin preservando hostname #
| |
4. Comparar status e headers #
| |
5. Correlacionar com logs do origin #
Se a requisição pública nem chega ao Nginx, isso é outra pista forte de que a resposta foi gerada antes.
6. Investigar a camada onde o comportamento divergiu #
Só então entrar em:
- firewall rules;
- WAF events;
- bot/challenge;
- rate limiting;
- cache;
- redirects;
- política de path/hostname.
Por que eu tirei o “429” do título deste case #
A pauta original previa 403 ou 429 porque os dois status são comuns em borda.
A fonte técnica recuperada para este caso prova 403, inclusive com header de challenge.
Ela não prova um 429 nesta mesma ocorrência.
Então o artigo ficou mais estreito.
É uma decisão editorial simples:
melhor um título menor que a evidência sustenta do que uma cobertura maior inventada por plausibilidade.
O aprendizado #
O origin responder 200 não significa que o usuário vai receber 200.
E o usuário receber 403 não significa que a aplicação respondeu 403.
Em uma cadeia com CDN, WAF, reverse proxy e aplicação, o troubleshooting mais eficiente é comparar a mesma requisição em vários saltos e descobrir onde o status mudou pela primeira vez.
No caso analisado, o origin respondia 200 e a borda pública devolvia 403 com sinal explícito de challenge.
A partir daí, continuar mexendo na aplicação seria investigar a camada errada.
Me chama.
Manda o contexto, os sintomas e o que já foi testado. Bora organizar as evidências antes de sair mexendo.