A Falsificação de Requisições Entre Sites, frequentemente abreviada como CSRF (Cross-Site Request Forgery) ou XSRF, representa uma vulnerabilidade crítica em aplicações web. Esse tipo de ataque explora a confiança que um site tem no navegador de um usuário já autenticado, forçando o navegador a enviar requisições maliciosas sem o conhecimento ou consentimento do usuário. O servidor web, ao receber essas requisições, as interpreta como legítimas, pois parecem vir de uma sessão autanticada.
Ao contrário dos ataques XSS (Cross-Site Scripting), que exploram a confiança do usuário em um site específico, o CSRF explora a confiança que o site tem no navegador do usuário. Um atacante não rouba diretamente os dados da sessão do usuário, mas sim utiliza a sessão existente para induzir o navegador a realizar ações indesejadas em nome do usuário. Exemplos encluem transferências de fundos, alterações de senha, envio de mensagens indesejadas ou até mesmo a adição de novos administradores a um sistema.
Compreendendo o Funcionamento do CSRF
Para entender o CSRF, é fundamental compreender como os cookies e as sessões funcionam em um navegador:
- Cookies de Primeira Parte: São definidos pelo domínio que você está visitando. Por exemplo, ao acessar
www.exemplo.com, os cookies definidos porwww.exemplo.comsão cookies de primeira parte e só podem ser lidos por páginas nesse mesmo domínio. - Cookies de Terceira Parte: São definidos por um domínio diferente daquele que você está visitando. Por exemplo, se
www.exemplo.comcarrega um recurso (como uma imagem) dewww.servidor-externo.com, este último pode definir um cookie. No entanto, esse cookie só será acessível porwww.servidor-externo.com. Embora o ataque CSRF não dependa diretamente da leitura de cookies de terceiros, a persistência de cookies de primeira parte é crucial para o sucesso da exploração.
A sequência típica de um ataque CSRF ocorre da seguinte forma:
- O usuário (vítima) autentica-se em um site legítimo (Site A), como um banco. O Site A gera e envia um cookie de sessão para o navegador do usuário.
- Enquanto a sessão do usuário no Site A ainda está ativa, o usuário é induzido a visitar um site malicioso (Site B), que pode ser um site de phishing, um anúncio ou qualquer página controlada pelo atacante.
- O Site B contém um código malicioso (por exemplo, um formulário HTML oculto ou uma tag
<img>) que força o navegador do usuário a enviar uma requisição para o Site A. - O navegador do usuário, por padrão, anexa automaticamente os cookies de sessão válidos para o Site A a esta requisição, mesmo que ela tenha sido iniciada pelo Site B.
- O Site A recebe a requisição com os cookies de sessão do usuário e a considera legítima, executando a ação solicitada pelo atacante (por exemplo, uma transferência de fundos), sem que o usuário tenha conhecimento.
Para que um ataque CSRF seja bem-sucedido, duas condições são essenciais:
- O usuário deve estar logado no site alvo (Site A) e ter uma sessão ativa, com cookies armazenados no navegador.
- O usuário deve visitar o site malicioso (Site B) enquanto a sessão no Site A ainda estiver ativa.
Tipos Comuns de Ataques CSRF
Os ataques CSRF podem ser implementados de diversas formas, dependendo do método HTTP que a aplicação vulnerável espera:
Ataques CSRF do Tipo GET
Este é o tipo mais simples de CSRF, explorando endpoints que executam ações sensíveis através de requisições GET. Um atacante pode incorporar uma requisição GET maliciosa em uma tag HTML, como <img>, <script> ou <link>, em uma página web controlada pelo atacante. Quando o navegador da vítima carrega essa página, ele automaticamente tenta carregar o recurso, enviando a requisição GET para o site alvo com os cookies da vítima.
<!-- Exemplo de ataque CSRF via GET -->
<img src="http://banco.exemplo.com/transferencia?contaDestino=atacante&valor=1000000" style="display:none;">
Se o usuário estiver autenticado em banco.exemplo.com, a requisição acima será enviada, potencialmente transferindo fundos para a conta do atacante sem o consentimento do usuário.
Ataques CSRF do Tipo POST
Embora as requisições POST sejam geralmente consideradas mais seguras que GET para operações que modificam dados, elas ainda são vulneráveis ao CSRF. Um atacante pode criar um formulário HTML oculto no seu site malicioso e usar JavaScript para submetê-lo automaticamente quando a vítima visita a página.
<!-- Exemplo de ataque CSRF via POST -->
<html>
<body onload="document.forms[0].submit()">
<form action="http://banco.exemplo.com/transferencia" method="POST">
<input type="hidden" name="contaDestino" value="atacante" />
<input type="hidden" name="valor" value="1000000" />
</form>
</body>
</html>
O navegador da vítima, novamente, incluirá os cookies de sessão válidos para banco.exemplo.com na requisição POST, permitindo que a ação seja executada.
Alvos de Ataques CSRF
É crucial entender que os ataques CSRF visam operações que resultam em alterações de estado no servidor. Isso inclui ações como:
- Transferências monetárias
- Alterações de senha ou e-mail
- Exclusão de conta ou dados
- Publicação de conteúdo (posts, comentários) em nome do usuário
Requisições que apenas recuperam dados (por exemplo, visualizar o saldo de uma conta) geralmente não são alvos de CSRF, pois o atacante não pode ler a resposta do servidor devido à Política de Mesma Origem (Same-Origin Policy) dos navegadores. Portanto, a proteção CSRF deve focar em operações de escrita (criação, atualização, exclusão).
Estratégias de Defesa Contra CSRF
A mitigação eficaz de ataques CSRF envolve a implementação de mecanismos que garantam que as requisições sensíveis sejam originadas apenas pelo usuário de forma intencional e a partir da aplicação legítima. As estratégias mais comuns incluem:
1. Validação do Cabeçalho HTTP Referer
O cabeçalho HTTP Referer indica a URL da página de onde a requisição foi feita. Uma estratégia de defesa é verificar se o Referer da requisição corresponde ao domínio da própria aplicação. Se a requisição vier de um domínio externo, ela pode ser bloqueada.
// Exemplo de filtro Java para validação de Referer
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String origemReferer = req.getHeader("Referer");
// Considera apenas requisições que modificam estado
if (req.getMethod().equalsIgnoreCase("POST") || req.getMethod().equalsIgnoreCase("GET")) {
if (origemReferer == null || !origemReferer.startsWith("http://appseguro.com")) { // Exemplo de domínio
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, "Requisição CSRF detectada!");
return;
}
}
chain.doFilter(request, response);
}
Limitações:
- O
Refererpode ser suprimido por alguns navegadores ou proxies, ou por configurações de privacidade do usuário, resultando em falsos positivos. - Alguns navegadores mais antigos tinham vulnerabilidades que permitiam a manipulação do
Referer. - A validação de substring pode ser enganosa (ex:
http://appseguro.com.malicious.site/).
2. Uso de Tokens Anti-CSRF (Tokens Sincronizadores)
Esta é a defesa mais robusta e amplamente recomendada. Consiste em gerar um token criptograficamente seguro e imprevisível a cada sessão de usuário ou por formulário, incorporá-lo às requisições e validá-lo no servidor. O token é geralmente armazenado na sessão do usuário no servidor (ou em um cookie seguro) e também incluído no formulário ou nos parâmetros da URL (para GET) ou corpo (para POST) da requisição.
Fluxo:
- O servidor gera um token CSRF único e aleatório quando a página é carregada.
- Este token é incluído em um campo oculto nos formulários HTML ou como parte da URL em links sensíveis. Ele também é armazenado na sessão do usuário no servidor.
- Quando o usuário submete o formulário ou clica no link, a requisição inclui o token.
- O servidor compara o token recebido na requisição com o token armazenado na sessão do usuário.
- Se os tokens coincidirem, a requisição é considerada legítima. Caso contrário, é um ataque CSRF e a requisição é rejeitada.
// Exemplo de filtro Java para validação de token CSRF
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession(false); // Não cria nova sessão se não existir
String tokenSessao = (session != null) ? (String) session.getAttribute("csrf_token") : null;
if (tokenSessao == null) {
// Gerar e armazenar novo token para a sessão
tokenSessao = UUID.randomUUID().toString();
if (session == null) {
session = req.getSession(true); // Cria sessão se necessário
}
session.setAttribute("csrf_token", tokenSessao);
}
String tokenParametro = req.getParameter("csrf_token");
String tokenHeader = req.getHeader("X-CSRF-Token"); // Para requisições AJAX
// Apenas valida tokens para métodos que modificam estado (POST, PUT, DELETE)
String metodo = req.getMethod();
if ("POST".equalsIgnoreCase(metodo) || "PUT".equalsIgnoreCase(metodo) || "DELETE".equalsIgnoreCase(metodo)) {
if (tokenSessao == null || (tokenParametro == null && tokenHeader == null) ||
(!tokenSessao.equals(tokenParametro) && !tokenSessao.equals(tokenHeader))) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, "Token CSRF inválido!");
return;
}
}
chain.doFilter(request, response);
}
No lado do cliente (HTML/JavaScript), o token deve ser injetado nos formulários e URLs:
// Assume que 'globalCsrfToken' é obtido do servidor e está disponível
function inserirTokensCsrf() {
// Adiciona token a formulários
document.querySelectorAll('form').forEach(form => {
if (form.method.toUpperCase() === 'POST' || form.method.toUpperCase() === 'PUT' || form.method.toUpperCase() === 'DELETE') {
let inputToken = document.createElement('input');
inputToken.type = 'hidden';
inputToken.name = 'csrf_token';
inputToken.value = globalCsrfToken; // Obtido de um meta tag, variável global, etc.
form.appendChild(inputToken);
}
});
// Adiciona token a links (se usados para ações que modificam estado - evite isso!)
document.querySelectorAll('a').forEach(link => {
let href = link.getAttribute('href');
if (href && href.includes('acao_sensivel') && !href.includes('csrf_token=')) { // Exemplo de condição
let sep = href.includes('?') ? '&' : '?';
link.setAttribute('href', href + sep + 'csrf_token=' + globalCsrfToken);
}
});
}
// Chame esta função após o carregamento do DOM
document.addEventListener('DOMContentLoaded', inserirTokensCsrf);
Para requisições AJAX, o token pode ser enviado em um cabeçalho HTTP personalizado:
// Exemplo com XMLHttpRequest para enviar token em cabeçalho
function enviarRequisicaoSegura(url, method, data) {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-CSRF-Token', globalCsrfToken); // Token no cabeçalho
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('Sucesso:', xhr.responseText);
} else {
console.error('Erro:', xhr.statusText);
}
};
xhr.onerror = function() {
console.error('Erro de rede.');
};
xhr.send(JSON.stringify(data));
}
// Exemplo de uso:
// enviarRequisicaoSegura('/api/salvar-dados', 'POST', { item: 'novo' });
3. Atributo SameSite para Cookies
O atributo SameSite em cookies, introduzido para mitigar o CSRF, controla quando os cookies são enviados em requisições cross-site. Existem três valores:
Strict: O cookie nunca é enviado em requisições cross-site, mesmo ao navegar para o site de origem através de um link. Garante alta segurança, mas pode prejudicar a experiência do usuário se a aplicação esperar cookies em navegações de outros sites.Lax: O cookie é enviado em requitações cross-site apenas para navegações de nível superior (GET) que alteram a URL, como um clique em um link<a>ou um formulário<form method="GET">. Não é enviado em requisições POST ou em outros tipos de carregamento de recursos cross-site (<img>,<script>). Oferece um bom equilíbrio entre segurança e usabilidade.None: O cookie é enviado em todas as requisições cross-site. Requer o atributoSecure(cookie deve ser enviado apenas via HTTPS). Não oferece proteção CSRF.
Configuração (em cabeçalhos HTTP):
Set-Cookie: ID_SESSAO=abcde12345; SameSite=Lax; Secure
Set-Cookie: token_seguro=xyz789; SameSite=Strict; Secure
Limitações:
- Compatibilidade do navegador: Embora amplamente suportado atualmente, a proteção depende da conformidade do navegador do usuário.
- O modo
Laxainda permite ataques CSRF via GET se a aplicação sensível usar GET para ações que modificam dados.
Outras Medidas Complementares
- Uso de Captchas: Para operações de alta sensibilidade (ex: alteração de senha, transferências financeiras), um Captcha pode exigir interação explícita do usuário, tornando o ataque automatizado inviável. No entanto, não é viável para todas as operações devido à usabilidade.
- Restrição de Métodos HTTP: Sempre use POST, PUT, DELETE para operações que modificam dados. Evite ao máximo usar GET para essas finalidades.
- Verificação de Credenciais do Usuário: Para operações críticas, exija que o usuário insira novamente sua senha ou um segundo fator de autenticação antes de prosseguir.
É fundamental lembrar que a defesa contra CSRF deve ser parte de uma estratégia de segurança em camadas. Uma implementação robusta de defesa CSRF não protege a aplicação contra outras vulnerabilidades, como XSS. Se um site for vulnerável a XSS, um atacante pode injetar scripts maliciosos que roubam tokens CSRF ou executam ataques CSRF diretamente no contexto do site legítimo, contornando as defesas.