Entendendo Cross-Site Request Forgery (CSRF ou XSRF).

Privacidade e Segurança

Entendendo Cross-Site Request Forgery (CSRF ou XSRF)

Os passos que você precisa seguir para aumentar a segurança de seus servidores web.

Danilo César
12 min readJun 9, 2021

--

No último artigo aprendemos como podemos prevenir alguns dos ataques Cross-Site Scripting (XSS) utilizando técnicas de sanitização adequadas em nossos servidores web. Agora, vamos compreender outra vulnerabilidade que também pode causar problemas em páginas web que não utilizam dos métodos adequados de segurança.

💡 Se você ainda não leu o último artigo desta série, “Requisições GET e POST de maneira segura usando PHP”, eu sugiro que você o faça, pois, alguns conceitos de Requisições HTTP, e de como estas funcionam, podem ser necessários para compreender os próximos conceitos — e, também, podem ajudar a aumentar a segurança de suas páginas web.

Introdução

Vamos investigar outra das vulnerabilidades mais comuns da web: a Cross-Site Request Forgery (CSRF), que engana usuários desavisados, fazendo-os executar ações indesejadas em outras páginas web em que estes estejam autenticados.

Para uma melhor ilustração do problema, vamos supor o seguinte cenário: você está logado em sua conta do banco, cujo servidor web não conhece as melhores práticas de desenvolvimento web; você notou uma transação estranha envolvendo uma pessoa ou empresa da qual nunca ouviu falar; na guia de outro navegador, você pesquisa o nome e acessa o site. Agora, mesmo se você não se autenticou ou se conectou à conta do seu banco nesta página da web desconhecida, nem usou um sistema (seguro) de challenge-response de terceiros, nossa página da web fictícia e mal-intencionada pode já ter alterado sua senha, transferido seu capital, ou realizado uma compra com suas credenciais.

Parece assustador, não é? Mesmo que a maioria dos navegadores de internet modernos estejam desenvolvendo a criação de “sandboxes”, e limitando o uso de cookies que não estão na política do mesmo site, existem muitos usuários na web utilizando navegadores de internet desatualizados, e clicando em todos os links que aparecem em seus monitores — alguns desses links afirmam que o usuário é o vencedor por entrar no site naquela data e hora específicas, ou devido a uma resposta em uma pesquisa da qual nem sequer ouviram falar.

Conversa utilizando latas e barbante.
Conversa utilizando latas e barbante. Imagem: Wannapik.

No passado, alguns dos sites mais acessados na internet sofreram algum tipo de ataque relacionado a CSRF, como o Facebook, Netflix, Gmail, YouTube, e o New York Times, mas também aplicações web, tais como o Mozilla Firefox e o Apache HTTP Server. De acordo com este paper, muitos desses já resolveram os problemas, e outros, graças à comunidade de desenvolvedores open-source, também os corrigiram.

Ao executar funções indesejadas na sessão de um usuário legítimo, esses agentes mal-intencionados usam seus links da web para realizar a ação que desejarem em nosso site, que já validou o cookie de sessão do usuário, e o armazenam na sessão. Essa é a pior parte do ataque CSRF: ele não depende exclusivamente do administrador do nosso site, mas também de como os navegadores funcionam, e do comportamento dos usuários.

Como o CSRF funciona

Vamos revisitar o nosso exemplo de página mal-intencionada que realizou um ataque sem o conhecimento do usuário.

A primeira condição para um ataque CSRF ser bem-sucedido é uma situação em que o usuário legítimo está logado em uma página web confiável, a qual mantém informações desta sessão como HTTP Cookies, que também asseguram verificações mais rápidas das credenciais do usuário, de modo que não é preciso que estes informem seu nome de usuário e senha a cada requisição ao servidor web.

De acordo com a MDN Web Docs, HTTP Cookies são tipicamente usados para indicar se duas requisições partiram de um mesmo navegador de internet. Também, elas mantêm informações acerca do protocolo HTTP, ou de sua versão criptografada, o protocolo HTTPS.

A segunda condição é uma requisição partindo de uma página web mal-intencionada que faz com que o navegador de internet do usuário envie uma requisição ao servidor web em que este realizou previamente a autenticação, executando uma requisição GET ou POST. Isto pode ser feito, por exemplo, ao criar um formulário web, utilizando HTML, cuja página de destino é uma página web insegura em um servidor web confiável.

Em termos mais simples, os ataques Cross-Site Request Forgery (CSRF) forjam uma requisição que está sendo feita ao servidor web confiável, realizando um “cruzamento de sites” (do inglês, “crossing sites”). A figura a seguir explica como um ataque CSRF funciona: a página mal-intencionada utiliza a sessão autenticada do usuário no navegador de internet para executar uma ação confiável em uma página de internet confiável.

Diagrama exibindo o fluxo de informações em um ataque CSRF.
Imagem: Souza, 2009.

Para os propósitos deste artigo, não iremos desenvolver este método em aplicações reais, porque nosso objetivo não é explorar vulnerabilidades em quaisquer serviços; na verdade, buscamos desenvolver melhores implementações para a internet.

Exemplo #1: método HTTP POST

Se a página de destino não está protegida contra ataques CSRF, aqueles maus agentes podem realizar o que bem entenderem com as credenciais do usuário. Por exemplo:

<html>
<body>
<form id="evil-form" action="http://my.trustful.bank/transfer?amount=123&account=stevie" method="POST">
<button type="submit">Click here</button>
</form>
</body>
</html>

Neste exemplo, assuma que esta página realmente existe na internet, de modo que trustful.bank utiliza uma requisição HTTP para enviar a quantia de 123 dólares a um cliente identificado como stevie, através da página /transfer-funds.

💡 Esta não é uma boa prática, portanto acredito que seu banco (real) está seguro, e protegido contra isto. Se não estiver, assim como a qualquer outro servidor web, as consequências podem ser devastadores, o que pode gerar alguns problemas legais, uma vez que este não esteja cumprindo com o que preceitua a maioria das Normas de Privacidade e Proteção de Dados, como a GDPR 🇪🇺 e a LGPD 🇧🇷 atualmente.

Exemplo #2: Comportamento automático

Aqueles maus agentes nem sequer precisam que o usuário interaja diretamente com o botão de enviar dados para alcançarem os resultados esperados. Eles podem, por exemplo, alterar o código-fonte para um evento onload que é executado assim que o navegador do usuário renderizar a página web, desta maneira:

<html>
<body onload="document.getElementById('evil-form').submit();">
<form id="evil-form" action="http://my.trustful.bank/transfer" method="POST">
<input type="hidden" name="account" value="stevie"></input>
<input type="hidden" name="amount" value="123"></input>
<button type="submit">Click here</button>
</form>
</body>
</html>

Ademais, muitos navegadores de internet suportam as requisições HTTP GET e POST, de modo que os ataques CSRF deverão funcionar em ambos os métodos.

Exemplo #3: Sem web forms

Para piorar a situação, os maus agentes não estão limitados a formulários web com HTML. Eles podem utilizar, por exemplo, uma simples tag img, como esta:

<html>
<body>
<img src="http://my.trustful.bank/transfer?amount=123&to=stevie" />
</body>
</html>

Este ataque poderá, também, forçar o usuário a seguir um redirecionamento, caso inserido no httpd.conf ou no arquivo .htaccess do servidor web, como nestes exemplos retirados do livro “XSS Attacks: Cross Site Scripting Exploits and Defense” (2007):

Redirect 302 /a.jpg https://somebank.com/transferfunds.asp?amnt=1000000&acct=123456

O resultado produzido será uma requisição como esta:

GET /a.jpg HTTP/1.0
Host: ha.ckers.org
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
Accept: image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://somebank.com/board.asp?id=692381

E a seguinte resposta do servidor:

HTTP/1.1 302 Found
Date: Fri, 23 Mar 2007 18:22:07 GMT
Server: Apache
Location: https://somebank.com/transferfunds.asp?amnt=1000000&acct=123456
Content-Length: 251
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title></head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://somebank.com/transferfunds.asp?amnt=1000000&amp;acct=123456">here</a>.</p>
</body></html>

Neste exemplo, assim que o navegador de internet realizar o redirecionamento, o usuário será encaminhado para o local de destino com os HTTP Cookies intactos, porém sem que a URL seja alterada para a página de destino, o que torna a situação ainda pior, porque o usuário poderá não ser capaz de facilmente detectar as URLs neste caso.

Quem poderia imaginar que uma única linha de código poderia causar tanto problema, não é mesmo? Por isso, não se esqueça: segurança na internet nunca é demais, e sempre há algo mais para aprender e aplicar.

Ataques CSRF e/ou XSS

Os ataques Cross-Site Scripting (XSS) e Cross-Site Request Forgery (CSRF) têm alguns elementos em comum, mas não se confundem. Ainda, ambos podem ser utilizados e implementados de maneira conjunta.

Um exemplo desta combinação foi o “MySpace Worm” (também conhecido por “Samy worm”, ou “JS.Spacehero worm”), desenvolvido por Samy Kamkar, à época um desenvolvedor de 19 anos, que, em 2005, desenvolveu um script ao adicionar algumas linhas de código que “infectavam” os perfis de outros usuários na rede social para torná-los seus amigos, e rapidamente saiu do controle, fazendo com que ele alcançasse quase um milhão de pedidos de amizade.

Fragmento de um perfil na rede social MySpace, em 2005.
Fragmento do perfil de Kamkar no MySpace, exibindo quantos amigos ele possuía após seu script, em 2005. Imagem: VICE.

Embora seu ataque tenha sido inofensivo, um mau agente poderia ter injetado um código malicioso que comprometeria todo o servidor web, se ninguém tivesse notado ou levado a sério a ameaça.

💡 O Cross-Site Request Forgery é também conhecido por XSRF, além de CSRF, seguindo a regra do acrônimo para Cross-Site Scripting (XSS). A razão mais provável para isso é com fins de evitar a confusão com Cascading Style Sheets, utilizado para estilizar páginas web, cujo acrônimo também é CSS.

Como prevenir ataques CSRF

Então, como podemos prevenir ataques CSRF? Há algumas ações que precisamos realizar:

1. Mantenha o navegador de internet atualizado

Você ficaria surpreso com a quantidade de usuários que continuam utilizando navegadores de internet (browsers) desatualizados, bem como outras aplicações, no dia-a-dia. As razões para isto são inúmeras, dentre elas a falta de informação (de como fazê-lo, e porquê), a compatibilidade com uma versão específica (há muitas situações em que a retrocompatibilidade não existe), ou ainda por especificações dos contratos de suas empresas — e não me refiro apenas a navegadores de internet.

Como usuário, a primeira medida a tomar é manter seu navegador de internet atualizado. As aplicações mais utilizados do mercado fazem uso do WebKit, Gecko, ou de outra browser engine que está sendo atualmente mantida e desenvolvida por uma comunidade aberta de desenvolvedores. Eles estão cientes destes problemas, e comprometidos com a solução destes em curto prazo. Algumas das empresas por trás dos browsers mais utilizados no mercado também possuem programas de “bug bounty”, os quais recompensam pesquisadores de segurança que encontrarem um bug que possa comprometer os dados e a privacidade de seus usuários.

Se você é um desenvolvedor, você deveria alertar seus usuários de que aplicações desatualizadas podem causar problemas, inclusive ataques CSRF, de modo que estes usuários podem estar expondo seus dados pessoais a maus agentes na internet. Como um bônus, esta prática pode ajudar você a entregar uma melhor experiência de usuário, visto que navegadores de internet atualizados também incorporam novas funcionalidades e APIs que aumentam a usabilidade em muitas páginas web.

Aliás, recentemente, Google e Mozilla anunciaram algumas melhorias no âmbito da segurança de suas browsers engines, tais como a “privacy sandbox”, melhores políticas de HTTP Cookies, e de mecanismos de bloqueio para JavaScript.

2. Verifique o HTTP Referrer header

A maioria das requisições em browsers modernos incluem dois metadados que podem nos ajudar a validar de onde é a fonte daquela requisição: a Origin e o Referrer contidos no cabeçalho desses dados.

Como desenvolvedor, você pode verificar, assim que uma requisição é feita para o seu servidor web, se os dados do cabeçalho (do inglês, “header data”) Origin e Referrer partiram de um mesmo local. Se não, você pode ignorá-la, e não seguir com qualquer função partindo desta Origin.

Infelizmente, há situações em que isto não será possível, e você pode acabar por potencialmente bloquear requisições legítimas partindo de um usuário que utiliza um proxy corporativo ou outros recursos similares. Também, há muitas maneiras de forjar estas informações de cabeçalho, por esta razão muitos autores afirmam não ser esta a melhor prática para proteger servidores web de ataques CSRF.

3. Implemente o atributo SameSite

O atributo SameSite (RFC6265bis) pode realmente nos ajudar a mitigar ataques CSRF, pois uma página web desautorizada não seria capaz de completar sua requisição ao nosso servidor web se esta for uma requisição “cross-site”.

Com fins de tornar nossos HTTP Cookies restritos à localização “de um mesmo-site”, podemos implementar este atributo, setando-o no cabeçalho da resposta à requisição HTTP. Assim, nosso HTTP Cookies pode ser estrito a um contexto primário (do inglês, “first-party”) ou de mesmo site (do inglês, “same-site”). Por exemplo:

Set-Cookie: TOKEN=1bf3dea9fbe265e40d3f9595f2239103; Path=/; SameSite=lax

De acordo com a MDN Web Docs, o atributo SameSite pode aceitar um destes três valores:

  • Lax — padrão se nenhum atributo SameSite for especificado; HTTP Cookies podem ser enviados quando o usuário navega ao site de destino daquele cookie. Estes não serão enviados em sub-requisições cross-site (por exemplo, para o carregamento de imagens ou frames em um site de terceiro), mas serão enviados quando um usuário navega para o site de origem (por exemplo, ao clicar em um link).
  • NoneHTTP Cookies serão enviados em todos os contextos, e podem ser remetidos para requisições de origem e cross-site requests. Este valor só deve ser utilizado em contextos seguros, como quando o atributo Secure também estiver definido.
  • StrictHTTP Cookies só poderão ser enviados para o mesmo site de onde estes foram originados.

💡 Se você estiver lidando com dados pessoais sensíveis, tais como aqueles usados para autenticação de usuários, em seu servidor web, você deve setar um prazo de vida curto para os HTTP Cookies. Também, você deverá setar o atributo SameSite para Strict ou Lax, para que uma requisição não-autenticada não seja efetivamente remetida ao servidor web.

Observe que você deve utilizar o atributo SameSite juntamente com um anti-CSRF token, dado que HTTP Requests, especialmente os métodos GET, HEAD e POST, serão executados mesmo que a requisição não seja permitida, em algumas circunstâncias, e deverá retornar um código de erro HTTP em resposta. Ainda assim, neste contexto, uma requisição simples foi realizada e executeda no servidor. Felizmente, há outros meios de resolver este problema, como o utilizando em conjunto com um valor randômico gerado por métodos matemáticos complexos e mais seguros.

4. Adicione tokens randômicos

Uma das maneiras mais comuns de mitigar ataques CSRF é fazendo uso de um anti-CSRF token, isto é, um token único, secreto e aleatório enviado nas requisições ao servidor web. Assim que uma requisição é feita, o servidor web poderá checar estes dados: se eles coincidem, então poderá ocorrer o processamento da requisição; se não, a requisição poderá ser rejeitada.

Este token pode ser gerado para cada requisição, armazenado no servidor web, e então inserido na requisição do cliente — diretamente no formulário web, ou incluído na requisição HTTP — , de modo que será possível detectar requisições partindo de contextos desautorizados ao nosso servidor web.

Os maus agentes não poderão ler o token, se utilizado em conjunto com o atributo SameSite, e não poderão proceder com qualquer função em nossa página web se não possuírem o token que coincide com aquele previamente definido em nosso servidor web para esta requisição em específico.

Isto pode ser feito se especificarmos um anti-CSRF token no mesmo site que o servidor confiável, e o incluirmos em um novo formulário web em HTML, como o que segue:

<html>
<body>
<form id="good-form" action="http://my.trustful.bank/transfer" method="POST">
<input type="hidden" name="token" value="1bf3dea9fbe265e40d3f9595f2239103"></input>
<input type="text" name="account" value="stevie"></input>
<input type="text" name="amount" value="123"></input>
<button type="submit">Submit</button>
</form>
</body>
</html>

No lado do cliente, podemos setar um anti-CSRF token em PHP, assim:

<?php
$_SESSION['token'] = bin2hex(random_bytes(16)); // 1bf3dea9fbe265e40d3f9595f2239103
?>

Ainda no lado do cliente, se estivermos utilizando JavaScript, podemos adicionar um anti-CSRF token, e enviá-lo como uma informação X-Header em uma XMLHttpRequest. Por exemplo:

var token = readCookie(TOKEN);                       // Obtém o HTTP Cookie que definimos previamente, identificado como "TOKEN"
httpRequest.setRequestHeader('X-CSRF-Token', token); // Então, o enviamos como uma informação de cabeçalho "X-CSRF-Token"

Próximos passos 🚶

Como já mencionamos, segurança na internet nunca é demais, e sempre há algo mais para aprender e aplicar. Para construir aplicações mais seguras, não deixe de acompanhar os próximos artigos desta série, e ler as referências que seguem para compreender mais detalhes acerca das melhores práticas no desenvolvimento web.

Se restou alguma dúvida, ou se tens alguma sugestão de como construir aplicações mais seguras usando PHP, compartilha nos comentários. 📣

Referências

[1] Zeller, W., & Felten, E. W. (2008). Cross-site request forgeries: Exploitation and prevention. Bericht, Princeton University. https://www.cs.memphis.edu/~kanyang/COMP4420/reading/csrf.pdf.

[2] Souza, J. (2009). Cross-Site Scripting & Cross-Site Request Forgery. Brasília, Universidade de Brasília. https://cic.unb.br/~rezende/trabs/johnny.pdf.

[3] Seth Fogie, Jeremiah Grossman, Robert Hansen, Anton Rager, and Petko D. Petkov. XSS Attacks: Cross Site Scripting Exploits and Defense. Syngress, 2007.

[4] “Cross-Site Request Forgeries and You”, em Coding Horror: https://blog.codinghorror.com/cross-site-request-forgeries-and-you/.

[5] “Using HTTP cookies”, em MDN Web Docs (Mozilla Developer Network): https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies.

[6] “CSRF”, em MDN Web Docs (Mozilla Developer Network): https://developer.mozilla.org/en-US/docs/Glossary/CSRF.

--

--

Danilo César

Pesquisador de Direito Digital. Desenvolvedor Full Stack. Escreve sobre direito, inovação, e programação.