O que é um token de CSRF? Qual é a sua importância e como funciona?

Ok, pessoal, estou escrevendo um aplicativo Django e quero apenas uma idéia do que realmente é um token CSRF e como ele protege os dados. Os dados da postagem não são seguros se você não usar tokens csrf?

Eu sei como usar csrf_token mas eu só preciso de algumas informações como funciona.

Cross-Site Request Forgery (CSRF) em palavras simples

  • Suponha que você está atualmente conectado ao seu banco on-line em www.mybank.com
  • Suponha que uma transferência de dinheiro do mybank.com resulte em um pedido de (conceitualmente) o formulário http://www.mybank.com/transfer?to=;amount= . (O seu número de conta não é necessário, porque está implícito no seu login.)
  • Você visita www.cute-cat-pictures.org , sem saber que é um site malicioso.
  • Se o dono desse site sabe a forma da solicitação acima (fácil!) E adivinha corretamente que você está logado em mybank.com (requer alguma sorte!), Eles poderiam include em sua página um pedido como http://www.mybank.com/transfer?to=123456;amount=10000 (em que 123456 é o número da sua conta nas Ilhas Cayman e 10000 é uma quantia que anteriormente considerava estar contente por possuir).
  • Você recuperou a página www.cute-cat-pictures.org , então o seu navegador fará essa solicitação.
  • Seu banco não pode reconhecer essa origem da solicitação: seu navegador enviará a solicitação junto com seu cookie www.mybank.com e ela parecerá perfeitamente legítima. Lá vai o seu dinheiro!

Este é o mundo sem tokens CSRF .

Agora, para melhor, com tokens CSRF :

  • A solicitação de transferência é estendida com um terceiro argumento: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971 .
  • Esse token é um número random enorme, impossível de adivinhar, que o mybank.com includeá em sua própria página da Web quando o servirem. É diferente a cada vez que eles veem qualquer página para qualquer pessoa.
  • O atacante não é capaz de adivinhar o token, não é capaz de convencer o seu navegador a rendê-lo (se o navegador funcionar corretamente …) e, portanto, o invasor não poderá criar uma solicitação válida, porque as solicitações com o token errado (ou nenhum token) será recusado pelo www.mybank.com .

Resultado: você mantém suas 10000 unidades monetárias. Eu sugiro que você doe um pouco disso para a Wikipedia.

(Sua milhagem pode variar.)

EDITAR a partir do comentário que vale a pena ler:

Vale a pena notar que o script de www.cute-cat-pictures.org normalmente não tem access ao seu token anti-CSRF de www.mybank.com por causa do controle de access HTTP. Essa nota é importante para algumas pessoas que enviam, sem motivo razoável, um header Access-Control-Allow-Origin: * para cada resposta do site sem saber para que serve, porque não podem usar a API de outro site.

Sim, os dados da postagem estão seguros. Mas a origem desses dados não é. Dessa forma, alguém pode enganar o usuário com o JS para fazer login no seu site, enquanto navega na página da Web do invasor.

Para evitar isso, o django enviará uma chave aleatória tanto no cookie quanto nos dados do formulário. Então, quando os usuários postarem, ele irá verificar se duas chaves são idênticas. No caso em que o usuário é enganado, o site de terceiros não pode obter os cookies do seu site, causando erro de autenticação.

O site gera um token exclusivo quando cria a página do formulário. Este token é necessário para postar / recuperar os dados para o servidor.

Como o token é gerado pelo seu site e é fornecido somente quando a página com o formulário é gerada, algum outro site não pode imitar seus formulários. Eles não terão o token e, portanto, não poderão postar no seu site.

A raiz de tudo é garantir que as solicitações sejam provenientes dos usuários reais do site. Um token csrf é gerado para os formulários e deve ser vinculado às sessões do usuário. Ele é usado para enviar solicitações ao servidor, no qual o token as valida. Esta é uma maneira de proteger contra o csrf, outra seria verificar o header do referenciador.

Deixe-me lhe dar um exemplo…

Imagine que você tenha um site como um Twitter simplificado, hospedado no a.com. Os usuários conectados podem inserir algum texto (um tweet) em um formulário que está sendo enviado para o servidor como uma solicitação POST e publicado quando eles clicam no botão de envio. No servidor, o usuário é identificado por um cookie contendo seu ID de session exclusivo, para que seu servidor saiba quem postou o Tweet.

O formulário pode ser tão simples assim:

  

Agora imagine, um bandido copia e cola esse formulário em seu site malicioso, digamos b.com. O formulário ainda funcionaria. Desde que um usuário esteja conectado ao seu Twitter (ou seja, ele tenha um cookie de session válido para a.com), a solicitação POST será enviada para http://a.com/tweet e processada normalmente quando o usuário clica no botão de envio.

Até agora, isso não é um grande problema, desde que o usuário tenha conhecimento do que a forma faz exatamente, mas e se o cara mau alterar a forma como esta:

  

Agora, se um de seus usuários acabar no site do bandido e clicar no botão “Clique para ganhar!”, O formulário será enviado ao seu site, o usuário será identificado corretamente pelo ID da session no cookie e o Tweet oculto será Publicados.

Se o nosso vilão fosse ainda pior, ele faria o inocente usuário enviar este formulário assim que ele abrisse sua página da web usando JavaScript, talvez até mesmo completamente escondido em um iframe invisível. Isso é basicamente uma falsificação de solicitação entre sites.

Um formulário pode ser enviado facilmente de qualquer lugar para qualquer lugar. Geralmente, esse é um recurso comum, mas há muito mais casos em que é importante permitir apenas que um formulário seja enviado do domínio ao qual ele pertence.

As coisas são ainda piores se o seu aplicativo da Web não fizer distinção entre solicitações POST e GET (por exemplo, no PHP usando $ _REQUEST em vez de $ _POST). Não faça isso! Os pedidos de alteração de dados podem ser enviados tão facilmente quanto http://a.com/tweet?tweet=This+isrealmente ruim , incorporado em um site malicioso ou até mesmo um e-mail.

Como posso ter certeza de que um formulário só pode ser enviado pelo meu próprio site? É aí que entra o token CSRF. Um token CSRF é uma string aleatória, difícil de adivinhar. Em uma página com um formulário que você deseja proteger, o servidor geraria uma string aleatória, o token CSRF, o adicionaria ao formulário como um campo oculto e também o lembraria de alguma forma, armazenando-o na session ou definindo um cookie contendo o valor. Agora o formulário ficaria assim:

  

Quando o usuário envia o formulário, o servidor simplesmente precisa comparar o valor do token CSRF (o nome não importa) com o token CSRF lembrado pelo servidor. Se as duas cadeias são iguais, o servidor pode continuar a processar o formulário. Caso contrário, o servidor deverá parar imediatamente de processar o formulário e responder com um erro.

Por que isso funciona? Existem várias razões pelas quais o vilão do nosso exemplo acima não consegue obter o token CSRF:

Copiar o código-fonte estático da nossa página para um site diferente seria inútil, porque o valor do campo oculto muda com cada usuário. Sem o site do bandido saber o token CSRF do usuário atual, ele sempre rejeitaria a solicitação POST.

Como a página maliciosa do vilão é carregada pelo navegador do usuário de um domínio diferente (b.com em vez de a.com), o vilão não tem chance de codificar um JavaScript, que carrega o conteúdo e, portanto, o token CSRF atual do usuário seu site. Isso ocorre porque os navegadores da web não permitem solicitações AJAX entre domínios por padrão.

O vilão também não consegue acessar o cookie definido pelo seu servidor, porque os domínios não coincidem.

Quando devo proteger contra falsificação de solicitações entre sites? Se você puder garantir que não misture GET, POST e outros methods de solicitação, conforme descrito acima, um bom começo seria proteger todas as solicitações POST por padrão.

Você não precisa proteger as solicitações PUT e DELETE, porque, conforme explicado acima, um formulário HTML padrão não pode ser enviado por um navegador usando esses methods.

JavaScript, por outro lado, pode realmente fazer outros tipos de pedidos, por exemplo, usando a function $ .ajax () do jQuery, mas lembre-se, para solicitações AJAX para trabalhar os domínios devem corresponder (contanto que você não configure explicitamente seu servidor web caso contrário) .

Isso significa que, muitas vezes, você nem precisa adicionar um token CSRF a solicitações AJAX, mesmo que sejam solicitações POST, mas você terá que se certificar de ignorar somente a verificação CSRF em seu aplicativo da Web se a solicitação POST for realmente uma Pedido AJAX. Você pode fazer isso procurando a presença de um header como X-Requested-With, que as solicitações AJAX geralmente incluem. Você também pode definir outro header personalizado e verificar sua presença no lado do servidor. Isso é seguro, porque um navegador não adicionaria headers personalizados a um envio regular de formulários HTML (veja acima), portanto, nenhuma chance para o Sr. Bad Guy simular esse comportamento com um formulário.

Se você estiver em dúvida sobre as requisições AJAX, porque por algum motivo você não pode procurar por um header como X-Requested-With, simplesmente passe o token CSRF gerado para seu JavaScript e adicione o token à requisição AJAX. Existem várias maneiras de fazer isso; adicione-o à carga útil como um formulário HTML normal ou adicione um header personalizado à solicitação AJAX. Desde que o seu servidor saiba onde procurá-lo em uma solicitação recebida e seja capaz de compará-lo com o valor original que ele lembra da session ou do cookie, você está classificado.