WEB3DEV

Cover image for Entendendo os ataques CSRF
Rafael Ojeda
Rafael Ojeda

Posted on • Atualizado em

Entendendo os ataques CSRF

Entendendo os ataques CSRF

Recentemente fui pesquisar sobre segurança na web enquanto escrevia Understanding Asynchronous JavaScript - queria ter certeza de que minhas recomendações eram seguras e não estou prestando um mau serviço a nenhum de meus alunos com minhas recomendações.

Infelizmente, os artigos na área de segurança eram bastante difíceis de entender. Havia muitas palavras que provocavam muito medo, incerteza e dúvida nos artigos. Eu fico emocionalmente em pânico quando leio esses artigos - e me preocupo que posso acabar fazendo algo errado - mesmo que a intenção destes artigos fosse boa!

Muitos artigos também não revelam todos os detalhes do CSRF, como criar um Ataque CSRF e como evitar um Ataque CSRF, o que me deixa confuso sobre o que aprendi. Eu acabo tendo que descobrir as coisas sozinho.

Quero facilitar a compreensão do CSRF, por isso, fiz um esforço ao escrever um artigo com informações completas (e passo a passo) sobre os Ataques de CSRF. Espero que este artigo lhe dê a clareza e confiança necessárias para construir apps web seguros.

Dois tipos de Ataques CSRF

Há dois tipos de Ataques CSRF:

  1. Normal CSRF
  2. Login CSRF

Veremos primeiro o Normal CSRF, em seguida o Login CSRF.

O que é um Ataque CSRF

Um Ataque CSRF é aquele que engana uma vítima e a faz enviar uma solicitação maliciosa - uma solicitação que ela não pretendia fazer - a um website onde ela está autenticada (logada).

A solicitação deve ter origem em outro website, que dá o nome "Cross-Site". Esta solicitação também imita um usuário autenticado, o que lhe dá o nome "Request Forgery".

Os Ataques CSRF são cegos - o que significa que o atacante não vê o que acontece depois que a vítima submete a solicitação. Portanto, os ataques CSRF frequentemente têm como alvo uma mudança de estado no servidor.

O que é uma mudança de estado? Basicamente, qualquer coisa que modifique o banco de dados é uma mudança de estado. Exemplos de mudanças de estado incluem:

  • Mudança de nome de usuário e senha
  • Envio de dinheiro para uma conta
  • Envio de mensagens falsas a partir da conta do usuário
  • Compartilhar imagens ou vídeos inapropriados da conta do usuário

Os ataques do CSRF aproveitam o fato de que os navegadores enviam cookies automaticamente para o servidor em cada solicitação. Sem qualquer proteção contra CSRF, o servidor pode assumir que uma solicitação é válida quando um cookie de autenticação está presente.

Os cookies de autenticação podem ser qualquer coisa, desde que o servidor os utilize para verificar se um usuário é válido. Pode ser um token de acesso. Também pode ser um ID de sessão. Depende de como o servidor lida com a autenticação.

Pré-requisitos para que o Ataque CSRF funcione

Há quatro pré-requisitos necessários para que um Ataque CSRF seja bem sucedido.

  • Uma solicitação de qualquer método é enviada para o servidor.
  • O usuário deve estar autenticado.
  • O servidor deve armazenar informações de autenticação em cookies.
  • O servidor não implementa técnicas de prevenção de CSRF (que serão discutidas abaixo).

Como funcionam os Ataques CSRF

Antes que um atacante possa lançar um Ataque CSRF, ele precisa encontrar uma solicitação consistente que possa atingir. Ele deve saber o que a solicitação faz. Esta pode ser qualquer solicitação - GET, POST, PUT, ou DELETE. Qualquer coisa serve.

Uma vez selecionada a solicitação a ser o alvo, ele deve gerar uma solicitação falsa para enganar o usuário.

Finalmente, ele deve induzir o usuário a enviar a solicitação. Na maioria das vezes, isto significa:

Encontrar uma forma de enviar a solicitação automaticamente sem que o usuário saiba. As abordagens mais comuns são através de tags de imagem e o envio automático de um formulário JavaScript.
falsificar um link (ou botão), o que induz o usuário a clicar nele. (também conhecido como Engenharia Social).

Ataques através de uma solicitação GET

Os Ataques CSRF com uma solicitação GET só funcionam se o servidor permitir que um usuário mude de estado com solicitações GET. Você não precisa se preocupar com este tipo de Ataque CSRF se suas solicitações GET forem somente de leitura.

Mas digamos que temos um servidor que não segue as melhores práticas de programação e permite mudanças de estado através de uma solicitação GET. Se eles fizerem isso, eles estarão em apuros - enormes problemas.

Por exemplo, digamos que há um banco que permite que você transfira dinheiro com o seguinte endpoint. Você só tem que entrar com account e amount na solicitação GET para enviar dinheiro a uma pessoa.

https://bank.com/transfer?account=Mary&amount=100
Enter fullscreen mode Exit fullscreen mode

O atacante pode gerar um link que envia o dinheiro para sua conta.

# Envia 9999 para a conta do atacante
https://bank.com/transfer?account=Attacker&amount=9999
Enter fullscreen mode Exit fullscreen mode

Neste ponto, o atacante pode encontrar uma maneira de acionar o link automaticamente sem que o usuário saiba.

Uma maneira é incluir o link em uma imagem 0x0 em uma página web ou em um e-mail. Se o usuário visitar esta página web ou e-mail, a solicitação GET é acionada automaticamente, uma vez que os navegadores e e-mails são configurados para buscar imagens automaticamente.

(Agora entendo porque os provedores de e-mail desabilitam o carregamento de imagens como uma precaução de segurança).

<!-- O download desta imagem desencadeia o ataque de solicitação GET -->
<img
  src="https://bank.com/transfer?account=Attacker&amount=9999"
  width="0"
  height="0"
  border="0"
/>
Enter fullscreen mode Exit fullscreen mode

Outra maneira é deturpar o que um link faz. Isto funciona porque as pessoas não verificam os links antes de clicar neles. Se a pessoa clicar no link, ela irá enviar a solicitação GET para o atacante sem saber.

<!-- link falso que desencadeia o ataque de solicitação GET -->
<a href="https://bank.com/transfer?account=Attacker&amount=9999"
  >View my Pictures</a
>
Enter fullscreen mode Exit fullscreen mode

Se o usuário estiver autenticado, o servidor receberá um cookie de autenticação que o faz acreditar que a solicitação é válida. Se o servidor não utilizar nenhum mecanismo de proteção CSRF, o dinheiro será enviado para o atacante.

Exemplos de Ataques CSRF com solicitações GET

uTorrent sofreu um Ataque CSRF em 2008, que permitiu mudanças de estado com solicitações GET.
O Youtube costumava ter uma vulnerabilidade de segurança em 2008 que permitia ao atacante realizar quase todas as ações possíveis para um usuário, inclusive enviar mensagens, adicionar a uma lista de amigos, etc.

Se você clicar nos links acima. Você poderá encontrar exemplos de solicitações GET reais que criam um Ataque CSRF. (Não se preocupe, nenhum link é perigoso aqui 😜).

Ataques CSRF com solicitação POST

Os Ataques CSRF com solicitação POST seguem o mesmo padrão - mas não podem ser enviados através de links ou tags de imagem. Eles precisam ser enviados através de um formulário ou através do JavaScript.

Vamos assumir que temos o mesmo endpoint vulnerável e que o atacante simplesmente precisa entrar com a informação de account e amount para acionar a solicitação..

POST https://bank.com/transfer?account=Attacker&amount=9999
Enter fullscreen mode Exit fullscreen mode

O atacante pode criar um formulário e ocultar os valores de account e amount do usuário. As pessoas que clicarem neste formulário falso enviaram a solicitação POST sem saber.

<!-- Formulário disfarçado como um botão! -->
<form action="https://bank.com/transfer" method="POST">
  <input type="hidden" name="acct" value="Attacker" />
  <input type="hidden" name="amount" value="9999" />
  <button>View my pictures</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Este formulário também pode ser executado automaticamente com o JavaScript sem que as pessoas saibam - usuários reais nem precisam clicar no botão, e já estão com problemas.

<form>...</form>
<script>
  const form = document.querySelector('form')
  form.submit()
</script>
Enter fullscreen mode Exit fullscreen mode

Ataques POST CSRF são assustadores, mas há maneiras de evitá-los. Falaremos sobre as técnicas na seção de prevenção abaixo.

Ataques CSRF com solicitações PUT e DELETE

Os Ataques CSRF não podem ser executados com solicitações PUT e DELETE porque as tecnologias que utilizamos não permitem que isso ocorra.

Sim. Você leu isso certo.

Os Ataques CSRF não podem ser executados através de formulários HTML porque os formulários não suportam solicitações PUT e DELETE. Ele só suporta GET e POST. Se você usar qualquer outro método (exceto GET e POST), os navegadores os converterão automaticamente em uma solicitação GET.

<!-- O formulário não envia uma solicitação PUT porque o HTML não suporta o método PUT. Isto se transformará em uma solicitação GET. -->
<form action="https://bank.com/transfer" method="PUT"></form>
Enter fullscreen mode Exit fullscreen mode

Portanto, não é possível executar um Ataque CSRF através de um HTML.

Agora, aqui está algo engraçado: como as pessoas enviam solicitações PUT e w através de um formulário se o HTML não o permite? Após algumas pesquisas, descobri que a maioria dos frameworks permitem que você envie uma solicitação POST com um parâmetro _method.

<!-- Como a maioria das estruturas lidam com as solicitações PUT -->
<form method="post" ...>
  <input type="hidden" name="_method" value="put" />
</form>
Enter fullscreen mode Exit fullscreen mode

Você pode executar um Ataque CSRF com solicitações PUT via JavaScript, mas o mecanismo de prevenção padrão em navegadores e servidores hoje em dia torna realmente difícil que estes ataques aconteçam - você tem que deliberadamente desativar as proteções para que isso aconteça.

Eis o porquê.

Para executar um Ataque CSRF com solicitações PUT, você precisa enviar uma solicitação de busca com o método put. Você também precisa incluir a opção credentials.

const form = document.querySelector('form')

// Envia a solicitação automaticamente
form.submit()

// Intercepta o envio do formulário e utiliza o Fetch para enviar um pedido AJAX em seu lugar.
form.addEventListener('submit', event => {
  event.preventDefault()
  fetch(/*...*/, {
    method: 'put'
  credentiials: 'include' // Inclui cookies na solicitação
 })
    .then(/*...*/)
    .catch(/*...*/)
})
Enter fullscreen mode Exit fullscreen mode

Isso não funcionaria devido a três razões.

Primeiro, esta solicitação NÃO será executada pelos navegadores automaticamente por causa do CORS. A menos - é claro - que o servidor crie uma vulnerabilidade ao permitir solicitações de qualquer pessoa com o seguinte cabeçalho:

Access-Control-Allow-Origin: *
Enter fullscreen mode Exit fullscreen mode

Segundo, mesmo que você permita que todas as origens acessem o seu servidor, você ainda precisa de uma opção Access-Control-Allow-Credentials para que os navegadores possam enviar cookies para o servidor.

_Access-Control-Allow-Credentials: true_
Enter fullscreen mode Exit fullscreen mode

Terceiro, mesmo se você permitir o envio de cookies para o servidor, os navegadores só enviarão cookies que tenham o sameSite definido para none. (Estes também são chamados third - party cookies ).

Se você não tem idéia do que estou falando a respeito do terceiro ponto, você está seguro - você realmente tem que ser um desenvolvedor malicioso que quer estragar seu servidor se você enviar cookies de autenticação como cookies third-party.

Esta seção é enorme. Eu criei mais alguns artigos para ajudar você a entender exatamente o que está acontecendo - e por que é tão difícil expor-se a um Ataque PUT CSRF:

Resumindo - você só tem que se preocupar com os Ataques POST CSRF a menos que realmente tenha estragado seu servidor

Métodos de prevenção CSRF

Os métodos de prevenção CSRF mais comuns atualmente são:

  • Padrão Double Submit Cookie
  • Método Cookie to header

Os dois métodos seguem a mesma fórmula.

Quando o usuário visita seu site, seu servidor deve criar um token CSRF e colocá-los nos cookies do navegador. Nomes comuns para este token são:

  • _CSRF-TOKEN
  • X-SRF-TOKEN
  • X-XSRF-TOKEN
  • X-CSRF-TOKEN_

Use qualquer um dos nomes de token que você quiser. Todos eles funcionam.

O importante é que o Token CSRF deve ser uma string gerada aleatoriamente e criptograficamente forte. Se você usar Node, você pode gerar a string com crypto.

import crypto from 'crypto'

function csrfToken (req, res, next) {
  return crypto.randomBytes(32).toString('base64')
}
Enter fullscreen mode Exit fullscreen mode

Se você utiliza Express, você pode colocar este token CSRF em seus cookies desta forma. Ao fazer isso, recomendo usar sameSite também. (Falaremos sobre o sameSite daqui a pouco).

import cookieParser from 'cookie-parser'

// Use isso para ler os cookies
app.use(cookieParser())

// Definição do Token CSRF para todos os endpoints
app.use(*, (req, res) => {
  const { CSRF_TOKEN } = req.cookies

 // Define o token se o usuário visitar esta página pela primeira vez nesta sessão
 if (!CSRF_TOKEN) {
  res.cookie('CSRF_TOKEN', csrfToken(), { sameSite: 'strict' })
 }
})
Enter fullscreen mode Exit fullscreen mode

Como você usa o Token CSRF, muda dependendo de como você subscreve o padrão de envio, com Double cookie ou o método Cookie to header (ou ambos).

Padrão Double Submit Cookies

O nome deste padrão é um pouco enganoso - porque parece significar o envio de um cookie duas vezes com "Double Submit Cookie".

O que isto realmente significa é:

Você envia o Token CSRF em um cookie
Você torna o <form> com um Token CSRF - que seria incluído na apresentação do formulário.
(Daí o duplo envio).

Se você usar Express, você pode passar o Token CSRF para o HTML desta forma:

app.get('/some-url', (req, res) => {
  const { CSRF_TOKEN } = req.cookies

  //Renderização com Nunjucks.
  // Substitua o Nunjucks por qualquer outro modelo de engine que você use
  res.render('page.nunjucks', {
    CSRF_TOKEN: CSRF_TOKEN
  })
})
Enter fullscreen mode Exit fullscreen mode

Então você pode usar CSRF_TOKEN desta forma:

<form>
  <input type="hidden" name="csrf" value="{{CSRF_TOKEN}}" />
  <!-- ... -->
</form>
Enter fullscreen mode Exit fullscreen mode

O servidor pode então verificar a validade da sessão, comparando dois Tokens CSRF. Se corresponderem, significa que a solicitação não é falsificada - porque não há maneira de um atacante adivinhar o valor do token CSRF em outro site.

// Verifica a validade do Token CSRF
app.post('/login', (req, res) => {
  const { CSRF_TOKEN } = req.cookies
  const { csrf } = req.body

  // Abortar a solicitação
  // Você também pode lançar um erro se desejar
  if (CSRF_TOKEN !== csrf) return

  // ...
})
Enter fullscreen mode Exit fullscreen mode

Método Cookie to Header

O método cookie to header é semelhante - exceto que é executado com o JavaScript. Neste caso, o Token CSRF deve ser incluído tanto no cookie quanto no cabeçalho da solicitação.

Neste caso, é necessário:

Definir credentials para include ou same-origin para incluir cookies
Pegue o token CSRF do document.cookies e adicione-o como um cabeçalho de solicitação.

Aqui está um exemplo de solicitação:

// Obtém o valor de um cookie nomeado
function getCookie () {
  const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
  if (match) return match[2]
}

// Envia o pedido
fetch('/login', (req, res) => {
  credentials: 'include',
  headers: {
    'CSRF_TOKEN': getCookie('CSRF_TOKEN')
 }
})
Enter fullscreen mode Exit fullscreen mode

O servidor pode verificar a validade do Token CSRF desta forma:

// Verifica a validade do Token CSRF
app.post('/login', (req, res) => {
  const { CSRF_TOKEN } = req.cookies
  const { CSRF_TOKEN: csrf } = req.headers

  // Abortar o pedido
  // Você também pode lançar um erro se desejar
  if (CSRF_TOKEN !== csrf) return

  // ...
})
Enter fullscreen mode Exit fullscreen mode

Facilite tudo isso com uma biblioteca

Eu te mostrei como criar e testar manualmente os Tokens CSRF porque eu queria dar a você uma compreensão do processo.

Este processo já foi resolvido muitas vezes, portanto não devemos fazê-lo manualmente (a menos que você esteja aprendendo, como o que eu fiz aqui).

Se você usar Express, eu recomendo o uso da biblioteca csurf, já que ela é mais robusta e flexível em comparação com o que eu mostrei no exemplo acima.

Atributo SameSite Cookie

A configuração do sameSite para strict no exemplo acima garante que o cookie CSRF Token só seja enviado ao servidor se a solicitação for originada do mesmo website. Isto assegura que o CSRF Token nunca será revelado para páginas externas.

Você pode - opcionalmente, mas recomendado - definir o atributo do sameSite para strict, assim como você define o cookie de autenticação. Isto assegura que nenhum Ataque CSRF possa ser conduzido, uma vez que o cookie de autenticação não será mais incluído nas solicitações entre sites.

Você precisa da proteção do token CSRF se você usou o sameSite para strict como seu cookie de autenticação?

Eu diria que não na maioria dos casos - porque o sameSite já protege o servidor de solicitações entre sites. Mas ainda precisamos do token CSRF para proteger contra um tipo particular de CSRF: Login CSRF.

Você pode ler mais sobre os cookies do sameSite neste artigo.

Login CSRF

Um Login CSRF é completamente diferente de um Ataque CSRF normal em termos de intenção.

Em um Login CSRF, o atacante engana um usuário para entrar no sistema com as credenciais do atacante. Quando o ataque é bem sucedido, o usuário continuará usando a conta do atacante se não estiver atento.

<form action="http://target/login" method="post">
  <input name="user" value="Attacker" />
  <input name="pass" type="password" value="AttackerPassword" />
  <button>Submit</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Eles também podem acionar o formulário automaticamente com JavaScript.

const form = document.querySelector('form')

// Envia a solicitação automaticamente
form.submit()
Enter fullscreen mode Exit fullscreen mode

Se o usuário não se der conta de que logou na conta do agressor, pode adicionar dados pessoais - como informações de cartão de crédito ou histórico de busca - à conta. Os atacantes podem então voltar a entrar em suas contas para visualizar estes dados.

O Google era vulnerável contra ataques de Login CSRF no passado.

Podemos impedir o Login CSRF com o padrão Double Submit Cookie mencionado acima - os atacantes não poderão adivinhar o Token CSRF, o que significa que não poderão lançar um Ataque de Login CSRF.

Encerrando

CSRF significa Cross Site Request Forgery (Solicitação de Falsificação Cruzada). Há dois tipos de Ataques CSRF:

Normal CSRF
Login CSRF

No Normal CSRF, o atacante tem como objetivo criar uma mudança de estado através de uma solicitação.

No CSRF Login, o atacante tem o objetivo de enganar o usuário para entrar na conta do atacante e, espera se beneficiar das ações do usuário se ele não estiver ciente.

Você pode evitar ambos os tipos de Ataques CSRF com o padrão Double Submit Cookie e o método Cookie to header. A configuração sameSite para strict impede o Normal CSRF, mas não o Login CSRF.

É isso aí!

Obrigado por ler. Este artigo foi originalmente publicado em meu blog. Assine minha newsletter se você quiser mais artigos para ajudá-lo a se tornar um melhor desenvolvedor de front-end.

Artigo escrito por Zell Liew e traduzido para o Português por Rafael Ojeda

Você pode encontrar o artigo original em inglês aqui

Top comments (0)