CSRF (Cross-Site Request Forgery): como acontece e por que é perigoso
CSRF ocorre quando um atacante faz o navegador da vítima enviar uma requisição autenticada para sua aplicação sem que a vítima perceba. Isso é possível porque, em muitos cenários, o navegador anexa automaticamente credenciais em requisições (principalmente cookies). Se a aplicação confiar apenas no fato de “o cookie veio junto”, ela pode executar ações sensíveis (ex.: trocar e-mail, alterar senha, transferir saldo) disparadas por uma página maliciosa.
Fluxo típico de ataque
- A vítima está logada no
https://app.exemplo.com(cookie de sessão válido). - Ela visita
https://site-malicioso.com. - O site malicioso dispara uma requisição para
https://app.exemplo.com/conta/email(por<form>auto-submit,<img>,fetchetc.). - O navegador envia o cookie junto (dependendo de políticas como SameSite e do tipo de requisição).
- Se o servidor não exigir uma prova adicional de intenção, a ação é executada.
Mitigações de CSRF
1) SameSite em cookies (primeira linha de defesa)
SameSite controla quando o navegador envia cookies em contextos cross-site. É uma mitigação importante, mas não deve ser a única em operações críticas.
- SameSite=Strict: cookie só é enviado em navegação “same-site”. Mais seguro, mas pode quebrar fluxos (ex.: login via redirecionamento, links vindos de outros sites).
- SameSite=Lax: cookie é enviado em navegação top-level (ex.: clicar em link) e em alguns GETs, mas não em POSTs típicos de formulários cross-site. Bom padrão para muitos casos.
- SameSite=None; Secure: cookie é enviado cross-site (necessário para alguns cenários como iframes/SSO), mas exige HTTPS. Nesse caso, proteção anti-CSRF vira obrigatória.
Exemplo de configuração de cookie (conceitual):
Set-Cookie: session=...; HttpOnly; Secure; SameSite=LaxObservação prática: SameSite reduz muito CSRF clássico via POST cross-site, mas não substitui token anti-CSRF em aplicações que precisam de SameSite=None ou que tenham endpoints sensíveis expostos a navegação cross-site.
2) Anti-CSRF token (sincronizador): passo a passo
Ideia: além do cookie, o cliente precisa enviar um token imprevisível que o atacante não consegue ler (por causa da política de mesma origem). O servidor valida esse token antes de executar a ação.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
Passo a passo (padrão sincronizador):
- Ao renderizar a página (ou ao fornecer um endpoint para obter token), o servidor gera um token e o associa à sessão do usuário.
- O token é inserido em formulários ou enviado em um header customizado em requisições AJAX.
- Em cada requisição mutável (POST/PUT/PATCH/DELETE), o servidor verifica se o token enviado corresponde ao token esperado para aquela sessão.
- Se faltar ou não bater: rejeitar com
403 Forbidden.
Exemplo (formulário):
<form method="POST" action="/conta/email"> <input type="hidden" name="csrf_token" value="..."> <input type="email" name="email"> <button>Salvar</button></form>Exemplo (AJAX com header):
fetch('/conta/email', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ email })});3) Double Submit Cookie: passo a passo
Ideia: o servidor emite um cookie com um valor aleatório (não HttpOnly, para que o front-end consiga ler) e o cliente envia o mesmo valor em um header ou campo. O servidor compara cookie vs header/campo. O atacante consegue forçar o envio do cookie, mas não consegue ler o valor para replicar no header/campo.
Passo a passo:
- Servidor define um cookie
csrfcom valor aleatório. - Front-end lê o cookie
csrfe envia o valor emX-CSRF-Token(ou campo oculto). - Servidor valida se
Cookie[csrf] == Header[X-CSRF-Token].
Exemplo (conceitual):
Set-Cookie: csrf=RANDOM; Secure; SameSite=Lax// cliente lê document.cookie e envia X-CSRF-Token: RANDOMCuidados: se houver XSS, o atacante pode ler esse cookie e burlar o esquema. Por isso, double submit cookie é uma mitigação contra CSRF, não contra XSS.
4) Verificação de Origin/Referer (quando aplicável)
Para requisições mutáveis, o servidor pode validar o header Origin (preferível quando presente) e, como fallback, Referer. Se a origem não for a esperada, rejeitar.
- Quando funciona bem: APIs e back-ends que recebem requisições de browsers modernos, especialmente em POST/PUT/DELETE.
- Limitações: alguns ambientes removem/alteram
Referer;Originpode não existir em certos fluxos; não substitui token em cenários complexos.
Checklist de validação:
- Aplicar em métodos mutáveis.
- Permitir apenas origens conhecidas (lista explícita).
- Falhar fechado (se header esperado não existir, decidir política: exigir token ou bloquear).
XSS (Cross-Site Scripting): como rouba tokens e sessões
XSS acontece quando a aplicação permite que conteúdo controlado por usuário seja interpretado como código no navegador (geralmente JavaScript). Com XSS, o atacante passa a executar scripts no contexto do seu domínio, podendo:
- Roubar dados acessíveis via JavaScript (ex.: tokens armazenados em
localStorage, dados exibidos na página). - Realizar ações em nome do usuário (mesmo que cookies sejam HttpOnly), disparando requisições autenticadas a partir da própria página comprometida.
- Capturar informações sensíveis digitadas (ex.: senha em um formulário falso).
Exemplo conceitual de XSS refletido
Um endpoint imprime um parâmetro sem escapar:
// anti-exemplo conceitual: renderiza ?q= diretamente no HTML<div>Resultados para: {q}</div>Se q for <script>...</script>, o navegador executa o script.
Mitigações de XSS
1) Escaping/encoding na saída (regra base)
O princípio é: dados não são código. Ao inserir dados em HTML, atributos, JavaScript inline ou URLs, aplicar o encoding correto para aquele contexto.
- Contexto HTML: escapar
< > & " '. - Contexto atributo: além de escapar, evitar concatenar atributos perigosos (ex.:
onerror). - Contexto JS: evitar interpolar strings em scripts; preferir dados via JSON seguro e APIs do DOM.
Prática recomendada: usar templates/frameworks que façam escaping por padrão e revisar pontos onde o escaping é desativado.
2) Sanitização (quando você precisa aceitar HTML)
Se o produto exige que usuários publiquem conteúdo com HTML (ex.: comentários com formatação), use sanitização com allowlist: permitir apenas tags e atributos seguros, removendo scripts, handlers e URLs perigosas.
- Preferir allowlist (permitir poucos) em vez de blocklist (tentar bloquear muitos).
- Sanitizar no servidor (fonte de verdade) e, se necessário, também no cliente para UX.
3) CSP (Content Security Policy) para reduzir impacto
CSP ajuda a bloquear execução de scripts injetados e reduzir vetores comuns (inline scripts, fontes externas não autorizadas). Uma política bem configurada pode impedir que um XSS “funcione” mesmo que a injeção aconteça.
Exemplo conceitual de cabeçalho (ajuste conforme seu app):
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'Pontos práticos:
- Evitar
'unsafe-inline'emscript-src. - Se precisar de inline, preferir nonces/hashes.
- Usar
frame-ancestorspara mitigar clickjacking (relacionado a abuso de sessão).
4) Redução da superfície de dados sensíveis
Mesmo com defesas, trate XSS como risco residual e minimize o que um script conseguiria exfiltrar:
- Evitar expor tokens em JavaScript quando possível.
- Não retornar dados sensíveis desnecessários em respostas (ex.: PII, segredos, claims excessivos).
- Cookies de sessão com
HttpOnlyreduzem roubo direto por JS, mas não impedem ações maliciosas via XSS.
Brute force e credential stuffing: diferenças e ameaças
- Brute force: tentativa repetida de adivinhar senha (muitas combinações) para um usuário.
- Credential stuffing: uso de credenciais vazadas (email/senha) em massa, explorando reutilização de senha. Aqui, a taxa de acerto pode ser alta mesmo com poucas tentativas por conta.
O objetivo das proteções é reduzir tentativas automatizadas, aumentar custo do ataque e detectar padrões anômalos sem bloquear usuários legítimos.
Mitigações para brute force/credential stuffing
1) Rate limiting (por IP, por conta e por rota)
Aplicar limites em endpoints sensíveis (login, reset de senha, verificação de OTP). Combine dimensões:
- Por IP: limita ataques concentrados.
- Por conta (identificador): limita ataques distribuídos contra um usuário específico.
- Por ASN/país (quando fizer sentido): detectar infra de bot/hosting.
Exemplo de política (conceitual):
| Dimensão | Limite | Ação |
|---|---|---|
| Por IP | 30 tentativas / 5 min | 429 + backoff |
| Por conta | 10 tentativas / 15 min | desafio adicional / bloqueio temporário |
| Por IP+conta | 5 tentativas / 5 min | bloqueio curto |
2) Backoff progressivo (atraso incremental)
Em vez de apenas bloquear, aumente o tempo de resposta ou imponha espera crescente após falhas consecutivas. Isso reduz eficiência de automação e diminui impacto em usuários que erram poucas vezes.
Passo a passo:
- Contar falhas recentes por conta e/ou IP.
- Aplicar atraso: 0s, 1s, 2s, 4s, 8s… com teto.
- Resetar contador após login bem-sucedido ou após janela de tempo.
3) Lockout inteligente (evitar DoS contra usuários)
Bloquear uma conta por muitas tentativas pode virar arma para o atacante (negação de serviço). Prefira lockout “inteligente”:
- Bloqueio temporário curto e escalonado.
- Bloqueio condicionado a sinais adicionais (IP suspeito, ASN de datacenter, ausência de cookies de dispositivo, alta taxa).
- Permitir recuperação segura (ex.: desafio adicional) em vez de bloqueio longo.
4) Detecção por IP/ASN, reputação e sinais de automação
Credential stuffing costuma vir de infra distribuída. Sinais úteis:
- ASN de provedores de cloud/hosting com histórico de abuso.
- Muitos logins para contas diferentes a partir do mesmo IP/ASN.
- Headers e fingerprints inconsistentes (ex.: user-agent rotativo, ausência de sinais de navegador real).
- Taxa de erro elevada e padrões de tentativa (listas).
Ação típica: exigir desafio adicional, reduzir limites, ou bloquear por janela curta.
5) Listas de senha vazada e políticas de senha orientadas a risco
Para reduzir sucesso de credential stuffing:
- Na criação/troca de senha, verificar se a senha está em listas de senhas vazadas/mais comuns.
- Bloquear senhas fracas e reutilizadas em vazamentos conhecidos.
- Combinar com alertas de login suspeito e recomendações de troca.
6) Alertas e telemetria de segurança
Sem visibilidade, você só descobre o ataque quando o dano já ocorreu. Instrumente:
- Eventos: falha de login, sucesso, reset de senha, mudança de email, mudança de 2FA.
- Alertas: picos de falha por rota, por IP/ASN, por conta.
- Notificações ao usuário: novo dispositivo/localização, múltiplas falhas, troca de credenciais.
Laboratório conceitual: qual proteção aplicar em diferentes arquiteturas
Objetivo: dado um cenário de autenticação, identificar quais ataques são mais prováveis e quais proteções são obrigatórias vs recomendadas.
Cenário A: Sessão com cookie (browser tradicional)
Arquitetura: o navegador autentica via cookie de sessão enviado automaticamente.
- CSRF: risco alto. Aplicar
SameSite(Lax/Strict quando possível) + anti-CSRF token (ou double submit) em rotas mutáveis. Considerar validação deOrigin. - XSS: risco alto. Aplicar escaping/encoding, sanitização onde houver HTML do usuário, CSP. Cookies
HttpOnlyajudam contra roubo direto de sessão, mas não impedem ações via XSS. - Brute force/stuffing: rate limiting por IP/conta, backoff, lockout inteligente, detecção por ASN, listas de senha vazada, alertas.
Cenário B: JWT em cookie (SPA + API, token enviado automaticamente)
Arquitetura: o JWT fica em cookie e é enviado automaticamente em requisições.
- CSRF: risco alto (mesma lógica do cookie de sessão). Aplicar
SameSite+ anti-CSRF token/double submit + validação deOriginem mutações. - XSS: ainda crítico. Se o cookie for
HttpOnly, reduz roubo do JWT via JS, mas XSS ainda pode executar ações autenticadas. CSP e escaping continuam essenciais. - Brute force/stuffing: mesmas medidas do cenário A.
Cenário C: JWT em header Authorization (token não é enviado automaticamente)
Arquitetura: o front-end adiciona Authorization: Bearer ... manualmente.
- CSRF: em geral, risco menor para endpoints que exigem header Authorization, porque um site atacante não consegue forçar o navegador a anexar esse header em uma requisição cross-site comum. Ainda assim, se houver endpoints que aceitam cookie ou outras credenciais automáticas, eles continuam sujeitos a CSRF.
- XSS: risco muito alto se o token for acessível ao JavaScript (ex.: armazenado em memória/armazenamento web). Um XSS pode exfiltrar o token e reutilizá-lo fora do navegador. Mitigações: escaping/encoding, CSP forte, sanitização e reduzir exposição de dados; evitar persistir token em locais facilmente acessíveis quando possível.
- Brute force/stuffing: mesmas medidas do cenário A, com atenção extra a endpoints de emissão/refresh de token.
Exercício guiado (checklist de decisão)
Para cada endpoint mutável (ex.: /transferencias, /conta/email):
- Ele aceita autenticação via cookie enviado automaticamente? Se sim: aplicar proteção anti-CSRF (SameSite + token e/ou Origin).
- Ele pode ser acionado por navegação cross-site (form/image/link)? Se sim: reforçar validação (método, content-type, Origin) e exigir token anti-CSRF.
- O front-end renderiza dados do usuário? Se sim: garantir escaping por padrão e sanitização quando HTML for permitido.
- O endpoint é alvo de tentativa automatizada (login/reset/OTP)? Se sim: rate limiting + backoff + detecção + alertas.
Tabela-resumo: ameaças x arquitetura
| Arquitetura | CSRF | XSS | Brute force / stuffing |
|---|---|---|---|
| Sessão em cookie | Alto (mitigar com SameSite + token + Origin) | Alto (escaping, CSP, sanitização) | Alto (rate limit, backoff, lockout inteligente, detecção) |
| JWT em cookie | Alto (mesmas defesas de cookie) | Alto (HttpOnly ajuda contra roubo, não contra ações) | Alto (principalmente emissão/refresh) |
| JWT em header | Menor (se somente header), mas cuidado com endpoints que aceitam cookie | Muito alto se token acessível ao JS (roubo/exfiltração) | Alto (proteger endpoints de autenticação) |