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


629

Estou escrevendo um aplicativo (Django, acontece) e só quero ter 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?


13
É um token secreto e específico do usuário em todos os envios de formulários e URLs de efeito colateral para impedir falsificações de solicitação entre sites. Mais informações aqui: en.wikipedia.org/wiki/Cross-site_request_forgery
— Robert Harvey

1
Parece que existe uma linha tênue entre proteger uma pergunta e bani-la por ser muito ampla: D
— anton1980 18/18/18

2
Da folha de dicas de prevenção do Forwery de solicitação entre sites da OWASP (CSRF) : " O script entre sites não é necessário para o CSRF funcionar. No entanto, qualquer vulnerabilidade de script entre sites pode ser usada para derrotar todas as técnicas de mitigação [...] do CSRF. Isso ocorre porque uma carga útil XSS pode simplesmente ler qualquer página do site usando um XMLHttpRequest [...]. É imperativo que nenhuma vulnerabilidade XSS esteja presente para garantir que as defesas do CSRF não possam ser contornadas. "
— toraritte

Respostas:


1497

Falsificação de solicitação entre sites (CSRF) em palavras simples

  • Suponha que você esteja atualmente conectado ao seu banco on-line em www.mybank.com
  • Suponha que uma transferência de dinheiro mybank.comresultará em uma solicitação (conceitualmente) do formulário http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>. (O número da sua 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 proprietário desse site souber a forma da solicitação acima (fácil!) E adivinhar corretamente que você está conectado mybank.com(requer um pouco de sorte!), Ele poderá incluir em sua página uma solicitação como http://www.mybank.com/transfer?to=123456;amount=10000(onde 123456está o número da conta das Ilhas Cayman) e 10000é uma quantia que você pensava anteriormente estar feliz em possuir).
  • Você recuperou essa www.cute-cat-pictures.orgpágina, então seu navegador fará essa solicitação.
  • Seu banco não pode reconhecer esta origem da solicitação: seu navegador da web enviará a solicitação junto com o seu www.mybank.comcookie e parecerá perfeitamente legítimo. Lá vai o seu dinheiro!

Este é o mundo sem tokens de CSRF .

Agora, o melhor com tokens CSRF :

  • O pedido de transferência é estendido com um terceiro argumento: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • Esse token é um número aleatório enorme e impossível de adivinhar que mybank.comserá incluído em sua própria página da web quando ele o servir. É diferente cada vez que eles veiculam uma página para alguém.
  • O invasor não é capaz de adivinhar o token, não é capaz de convencer seu navegador a entregá-lo (se o navegador funcionar corretamente ...) e, portanto, o invasor não poderá criar uma solicitação válida, porque solicita com o token errado (ou nenhum token) será recusado por www.mybank.com.

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

(Sua milhagem pode variar.)

EDIT do comentário que vale a pena ler:

Vale a pena notar que o script www.cute-cat-pictures.orgnormalmente não tem acesso ao seu token anti-CSRF www.mybank.comdevido ao controle de acesso HTTP. Esta observação é importante para algumas pessoas que enviam um cabeçalho Access-Control-Allow-Origin: *para cada resposta do site sem saber o que é, apenas porque não podem usar a API de outro site.


36
E, obviamente, o token seria idealmente chamado de token anti- CSRF, mas o nome provavelmente é complicado o suficiente.
— Lutz Prechelt 27/05

3
@LutzPrechelt obrigado. por que o javascript não pode obter tokens de autenticidade no navegador?
— precisa saber é o seguinte

72
Vale a pena notar que o script www.cute-cat-pictures.orgnormalmente não tem acesso ao seu token anti-CSRF www.mybank.comdevido ao controle de acesso HTTP. Esta observação é importante para algumas pessoas que enviam um cabeçalho Access-Control-Allow-Origin: *para cada resposta do site sem saber o que é, apenas porque não podem usar a API de outro site.
— sofe

9
@AugustinRiedinger Se o invasor abrir a página da Web em seu computador - como não possui o cookie do usuário conectado - ele não receberá o token csrf correspondente (cada token csrf deve ser válido apenas para uma sessão específica do usuário). Se o invasor tentar carregar a página da Web que contém o token no computador do usuário, com um script colocado no site cute-cat-pictures, o navegador impedirá que ele leia o www.mybank.com (e o token) por causa do mesma política de origem.
— Marcel

13
@LutzPrechelt Acho que não basta que o token seja sempre diferente, ele precisa ser emparelhado com uma sessão e o servidor deve verificar se o token que recebe foi gerado para uma sessão que o servidor identifica pelo cookie recebido. Caso contrário, o hacker pode simplesmente visitar o mybank e obter algum token válido. Portanto, se você usar um novo token em todos os formulários, precisará salvá-lo emparelhado com o id da sessão no servidor. Provavelmente é mais fácil usar o mesmo token por sessão.
— Marcel

222

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

Para evitar isso, o django enviará uma chave aleatória no cookie e nos dados do formulário. Então, quando os usuários POSTs, ele verifica se duas chaves são idênticas. Caso o usuário seja enganado, o site de terceiros não pode obter os cookies do site, causando erro de autenticação.


@DmitryShevchenko Oi, tentando entender como esse método de cookie + entrada de formulário é diferente de apenas validar o referenciador no lado do servidor? Todos os exemplos que encontro estão relacionados a um hacker que engana o usuário para postar de seu site no site real.
— Ethan

Ok, descobri por que o referenciador não é usado. Ele é bloqueado em muitos casos, pois é considerado para manter informações confidenciais às vezes. Empresas e seus procuradores normalmente fazem isso. No entanto, se o HTTPS for usado, é mais provável que ele não seja bloqueado.
— Ethan

4
É fácil mudar o referenciador, eu não diria que é uma informação confiável. O token CSRF, no entanto, é gerado usando a chave secreta do servidor e geralmente vinculado ao usuário
— Dmitry Shevchenko

1
Eu realmente não entendo por que isso é uma ameaça à segurança. O usuário será conectado a outro site ... mas o site original não terá como recuperar essas informações. Direita?
— Aakil Fernandes 13/08/14

6
Bem, suponha que eu injete um iframe malicioso de " bank.com/transfer?from=x&to=y " em um, digamos, no Facebook.com. Se você é cliente do bank.com e acessa o Facebook, esse iframe carregará a página do banco com seus cookies (porque o navegador os enviará para um domínio conhecido) e fará uma transferência de dinheiro. Sem você saber de nada.
— Dmitry Shevchenko

74

O site gera um token exclusivo ao criar a página do formulário. Esse token é necessário para postar / obter dados novamente no servidor.

Como o token é gerado pelo seu site e fornecido apenas quando a página com o formulário é gerado, outro site não pode imitar seus formulários - eles não terão o token e, portanto, não podem postar no seu site.


10
Um usuário poderia pegar a saída do token na fonte, pegar o cookie enviado a ele e depois enviar a partir de um site de terceiros?
— 23414 Jack Marchetti

9
@JackMarchetti yes. mas seria caro, pois toda vez que você desejasse enviar o formulário de um site de terceiros, seria necessário carregar a página e analisar o token. Os tokens CSRF deve ser idealmente em conjunto com outras formas de segurança Se você estiver preocupado com este vector de ataque
— tkone

4
Eu tenho a mesma pergunta que @JackMarchetti, o que não está claro é - se o token CSRF for alterado em cada login. Se permanecer o mesmo, o que impediria que um invasor se conectasse primeiro, pegando o token de solicitação e depois inserindo esse token no ataque?
— Paul Preibisch

7
@PaulPreibisch, ele deve mudar a cada carregamento da página - não a cada login. Dessa forma, o invasor precisará solicitar a página sempre que desejar enviar o formulário. Torna muito mais difícil.
— tkone

9
@ tkone, isso realmente não torna muito mais difícil. Se apenas dobra a quantidade de esforço e tempo. Não adiciona nenhum tipo de processamento proibitivo. O truque também está associando o token CSRF a um cookie específico do domínio e enviando esse cookie junto com o formulário. O cookie e os dados de postagem do formulário teriam que ser enviados ao servidor na solicitação POST. Dessa maneira, seria necessário um ataque de seqüestro de cookies para emular uma solicitação legítima.
— Pedro Cordeiro

56

O blog Cloud Under tem uma boa explicação dos tokens de CSRF.

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

O formulário pode ser tão simples quanto isso:

 <form action="http://a.com/tweet" method="POST">
   <input type="text" name="tweet">
   <input type="submit">
 </form> 

Agora imagine que 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 sessão válido para a.com), a solicitação POST será enviada para http://a.com/tweet e processada como de costume quando o usuário clicar no botão Enviar.

Até agora, isso não é um grande problema, desde que o usuário seja informado sobre o que exatamente o formulário faz, mas e se nosso bandido alterar o formulário da seguinte maneira:

 <form action="https://example.com/tweet" method="POST">
   <input type="hidden" name="tweet" value="Buy great products at http://b.com/#iambad">
   <input type="submit" value="Click to win!">
 </form> 

Agora, se um de seus usuários aparecer no site do bandido e clicar no botão "Clique para vencer!" , o formulário é enviado ao seu site, o usuário é identificado corretamente pelo ID da sessão no cookie e o Tweet oculto é publicado.

Se nosso bandido fosse ainda pior, ele faria o usuário inocente enviar este formulário assim que abrir sua página da Web usando JavaScript, talvez até completamente escondido em um iframe invisível. Basicamente, isso é falsificação de solicitação entre sites.

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

As coisas ficam ainda piores se seu aplicativo da web não faz 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<img src="http://a.com/tweet?tweet=This+is+really+bad"> , incorporados a um site mal-intencionado ou até a um email.

Como garantir que um formulário só possa ser enviado em meu próprio site? É aqui que o token CSRF entra. Um token CSRF é uma sequência aleatória e difícil de adivinhar. Em uma página com um formulário que você deseja proteger, o servidor geraria uma sequência aleatória, o token CSRF, o adicionaria ao formulário como um campo oculto e também se lembraria de alguma forma, seja armazenando-o na sessão ou definindo um cookie contendo o valor Agora, o formulário ficaria assim:

    <form action="https://example.com/tweet" method="POST">
      <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn">
      <input type="text" name="tweet">
      <input type="submit">
    </form> 

Quando o usuário envia o formulário, o servidor simplesmente precisa comparar o valor do campo publicado csrf-token (o nome não importa) com o token CSRF lembrado pelo servidor. Se as duas strings forem iguais, o servidor poderá continuar processando o formulário. Caso contrário, o servidor deve parar imediatamente de processar o formulário e responder com um erro.

Por que isso funciona? Há várias razões pelas quais o bandido 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 que o site do bandido conheça o token CSRF do usuário atual, seu servidor sempre rejeitará a solicitação POST.

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

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

Quando devo me proteger contra a falsificação de solicitações entre sites? Se você pode garantir que não combina GET, POST e outros métodos 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 solicitações PUT e DELETE, porque, como explicado acima, um formulário HTML padrão não pode ser enviado por um navegador usando esses métodos.

Por outro lado, o JavaScript pode realmente fazer outros tipos de solicitações, por exemplo, usando a função $ .ajax () do jQuery, mas lembre-se de que, para que as solicitações AJAX funcionem, os domínios devem corresponder (desde que você não configure explicitamente o servidor da Web) .

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

Se você tiver dúvidas sobre solicitações AJAX, porque, por algum motivo, não pode verificar um cabeçalho como X-Requested-With, basta passar o token CSRF gerado para o seu JavaScript e adicionar o token à solicitação AJAX. Existem várias maneiras de fazer isso; adicione-o à carga útil como faria um formulário HTML comum ou adicione um cabeçalho personalizado à solicitação AJAX. Desde que o servidor saiba onde procurá-lo em uma solicitação recebida e possa compará-lo com o valor original que lembra da sessão ou cookie, você será classificado.


Obrigado pela informação detalhada. Durante a solicitação de postagem, o site precisa enviar o token csrf para o servidor; portanto, quando o cliente enviará esse token csrf para o servidor? É ao fazer a solicitação de opções de comprovação? Por favor, elabore nesta parte .. #
— Sm Srikanth

@ Dan Como é que o b.com pode acessar os cookies de outro site a.com?
— zakir 9/02/19

8

A raiz de tudo isso é garantir que as solicitações sejam provenientes dos usuários reais do site. Um token csrf é gerado para os formulários e deve estar vinculado às sessões do usuário. É 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 cabeçalho do referenciador.


7
Não confie no cabeçalho do referenciador, ele pode ser falsificado facilmente.
— kag

3
Essa é a resposta correta! O token DEVE estar vinculado a uma sessão no servidor. Comparar dados de Cookie + Form como a resposta mais votada sugere que está completamente errado. Esses componentes fazem parte da solicitação, que o cliente constrói.
— Lee Davis

3
Na verdade não. O token DEVE estar vinculado a cada SOLICITAÇÃO ao servidor. Se você apenas vinculá-lo à sessão, corre o risco de alguém roubar o token da sessão e enviar uma solicitação com esse token. Portanto, para segurança máxima, o token deve estar vinculado a cada requisito http.
— Chrisl08:
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.