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?

Shawn
fonte
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.

Lutz Prechelt
fonte
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.

Dmitry Shevchenko
fonte
@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.

tkone
fonte
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.

Dan
fonte
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.

gladysbixly
fonte
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: