security - works - what csrf token




Por que é comum colocar tokens de prevenção de CSRF em cookies? (3)

Estou tentando entender todo o problema com o CSRF e formas apropriadas de evitá-lo. (Recursos que li, compreendi e concordei com: OWASP CSRF Prevention CHeat Sheet , Perguntas sobre CSRF .)

Pelo que entendi, a vulnerabilidade em torno do CSRF é introduzida pela suposição de que (do ponto de vista do servidor da Web) um cookie de sessão válido em uma solicitação HTTP recebida reflete os desejos de um usuário autenticado. Mas todos os cookies para o domínio de origem são magicamente anexados à solicitação pelo navegador, portanto, realmente todo o servidor pode inferir da presença de um cookie de sessão válido em uma solicitação que a solicitação vem de um navegador que possui uma sessão autenticada; não pode mais assumir nada sobre o código em execução nesse navegador ou se realmente reflete os desejos do usuário. A maneira de evitar isso é incluir informações de autenticação adicionais (o "token CSRF") na solicitação, transportadas por outros meios além do tratamento automático de cookies do navegador. Falando livremente, o cookie de sessão autentica o usuário / navegador e o token CSRF autentica o código em execução no navegador.

Portanto, em poucas palavras, se você estiver usando um cookie de sessão para autenticar usuários de seu aplicativo da web, você também deve adicionar um token de CSRF a cada resposta e exigir um token de CSRF correspondente em cada solicitação (mutante). O token CSRF faz uma viagem de ida e volta do servidor para o navegador de volta ao servidor, provando ao servidor que a página que faz a solicitação é aprovada por (gerada por, mesmo) esse servidor.

Sobre a minha pergunta, que é sobre o método de transporte específico usado para esse token CSRF nessa ida e volta.

Parece comum (eg em AngularJS , Django , Rails ) enviar o token CSRF do servidor para o cliente como um cookie (isto é, em um cabeçalho Set-Cookie), e então ter o Javascript no cliente arrancando-o do cookie e anexando-o como um cabeçalho XSRF-TOKEN separado para enviar de volta ao servidor.

(Um método alternativo é aquele recomendado por exemplo, Express , onde o token CSRF gerado pelo servidor é incluído no corpo de resposta via expansão de modelo do lado do servidor, anexado diretamente ao código / marcação que irá fornecer de volta para o servidor, por exemplo como uma entrada de formulário oculto. Esse exemplo é uma maneira mais web 1.0-ish de fazer as coisas, mas generalizaria bem para um cliente mais pesado JS.)

Por que é tão comum usar o Set-Cookie como o transporte downstream para o token CSRF / por que isso é uma boa ideia? Eu imagino que os autores de todos esses frameworks consideraram suas opções com cuidado e não entenderam isso errado. Mas à primeira vista, usar cookies para contornar o que é essencialmente uma limitação de design em cookies parece tolo. De fato, se você usasse cookies como transporte de ida e volta (Set-Cookie: cabeçalho downstream para o servidor informar ao navegador o token CSRF e Cookie: cabeçalho upstream para o navegador retorná-lo ao servidor) você reintroduziria a vulnerabilidade está tentando consertar.

Eu percebo que as estruturas acima não usam cookies para toda a viagem de ida e volta para o token CSRF; eles usam o downstream Set-Cookie e, em seguida, alguma outra coisa (por exemplo, um cabeçalho X-CSRF-Token) upstream, e isso fecha a vulnerabilidade. Mas mesmo usando o Set-Cookie como transporte a jusante é potencialmente enganoso e perigoso; o navegador agora anexará o token CSRF a todas as solicitações, incluindo solicitações genuínas de XSRF mal-intencionado; na melhor das hipóteses, o pedido é maior do que o necessário e, na pior das hipóteses, um código bem-intencionado, mas equivocado, do servidor pode realmente tentar usá-lo, o que seria muito ruim. Além disso, como o destinatário real do token CSRF é o Javascript do lado do cliente, isso significa que esse cookie não pode ser protegido apenas com http. Então, enviar o token CSRF para baixo em um cabeçalho Set-Cookie parece muito abaixo do ideal para mim.


Além do cookie de sessão (que é uma espécie de padrão), não quero usar cookies extras.

Eu encontrei uma solução que funciona para mim ao criar um aplicativo da Web de página única (SPA), com muitas solicitações AJAX. Nota: Estou usando o Java do lado do servidor e o JQuery do lado do cliente, mas não há coisas mágicas, então acho que esse princípio pode ser implementado em todas as linguagens de programação populares.

Minha solução sem cookies extras é simples:

Lado do Cliente

Armazene o token CSRF que é retornado pelo servidor após um login bem-sucedido em uma variável global (se você quiser usar o armazenamento da web em vez de um global, isso é bom, é claro). Instrua o JQuery a fornecer um cabeçalho X-CSRF-TOKEN em cada chamada AJAX.

A página principal "index" contém este snippet de JavaScript:

// Intialize global variable CSRF_TOKEN to empty sting. 
// This variable is set after a succesful login
window.CSRF_TOKEN = '';

// the supplied callback to .ajaxSend() is called before an Ajax request is sent
$( document ).ajaxSend( function( event, jqXHR ) {
  jqXHR.setRequestHeader('X-CSRF-TOKEN', window.CSRF_TOKEN);
});

Lado do servidor

Em um login successul, crie um token CSRF aleatório (e longo o suficiente), armazene-o na sessão do lado do servidor e retorne-o ao cliente. Filtre determinadas solicitações recebidas (confidenciais) comparando o valor do cabeçalho X-CSRF-TOKEN com o valor armazenado na sessão: elas devem corresponder.

Chamadas AJAX sensíveis (dados de formulário POST e GET JSON-data) e o filtro do lado do servidor capturando-os estão em um caminho / dataservice / *. As solicitações de login não devem atingir o filtro, portanto, elas estão em outro caminho. Solicitações de HTML, CSS, JS e recursos de imagem também não estão no caminho / dataservice / *, portanto, não são filtradas. Estes não contêm nada secreto e não podem causar danos, então está tudo bem.

@WebFilter(urlPatterns = {"/dataservice/*"})
...
String sessionCSRFToken = req.getSession().getAttribute("CSRFToken") != null ? (String) req.getSession().getAttribute("CSRFToken") : null;
if (sessionCSRFToken == null || req.getHeader("X-CSRF-TOKEN") == null || !req.getHeader("X-CSRF-TOKEN").equals(sessionCSRFToken)) {
  resp.sendError(401);
} else 
  chain.doFilter(request, response);
}

Meu melhor palpite quanto à resposta: considere estas 3 opções de como obter o token CSRF do servidor para o navegador.

  1. No corpo da solicitação (não um cabeçalho HTTP).
  2. Em um cabeçalho HTTP personalizado, não Set-Cookie.
  3. Como cookie, em um cabeçalho Set-Cookie.

Eu acho que o primeiro, o corpo da solicitação (embora demonstrado pelo Express ), não é tão portável para uma grande variedade de situações; nem todo mundo está gerando cada resposta HTTP dinamicamente; onde você acaba precisando colocar o token na resposta gerada pode variar muito (em uma entrada de forma oculta; em um fragmento de código JS ou uma variável acessível por outro código JS; talvez até mesmo em uma URL que parece geralmente um lugar ruim para colocar tokens CSRF). Portanto, embora seja viável com alguma personalização, o nº 1 é um lugar difícil de se fazer uma abordagem única.

O segundo, o cabeçalho personalizado, é atraente, mas na verdade não funciona, porque, embora o JS possa obter os cabeçalhos de um XHR invocado, ele não pode obter os cabeçalhos da página da qual foi carregado .

Isso deixa o terceiro, um cookie transportado por um cabeçalho Set-Cookie, como uma abordagem que é fácil de usar em todas as situações (o servidor de qualquer pessoa poderá definir cabeçalhos de cookie por solicitação, e não importa que tipo de os dados estão no corpo da solicitação). Portanto, apesar de suas desvantagens, era o método mais fácil para os frameworks implementarem amplamente.


Usar um cookie para fornecer o token CSRF ao cliente não permite um ataque bem-sucedido porque o invasor não pode ler o valor do cookie e, portanto, não pode colocá-lo onde a validação do CSRF do lado do servidor exige que ele seja.

O invasor poderá fazer uma solicitação ao servidor com o cookie do token de autenticação e o cookie do CSRF nos cabeçalhos da solicitação. Mas o servidor não está procurando o token de CSRF como um cookie nos cabeçalhos de solicitação, ele está procurando na carga útil da solicitação. E mesmo que o invasor saiba onde colocar o token de CSRF na carga, ele teria que ler seu valor para colocá-lo lá. Mas a política de origem cruzada do navegador impede a leitura de qualquer valor de cookie do site de destino.

A mesma lógica não se aplica ao cookie de token de autenticação, porque o servidor espera isso nos cabeçalhos de solicitação e o invasor não precisa fazer nada especial para colocá-lo lá.





owasp