Como proteger aplicações Node.js contra ataques CSRF
Victor Ikechukwu
17 de outubro de 2023
0 minutos de leituraUm ataque de falsificação de solicitação entre sites (CSRF) é uma vulnerabilidade de segurança que explora a confiança entre um navegador e um site legítimo. Atacantes ardilosos manipulam os navegadores para executar ações maliciosas em sites nos quais os usuários se autenticam e fazem login. Muitas vezes, esses ataques começam quando os usuários clicam em um link enviado por um e-mail fraudulento ou acessam um site comprometido, sem saber que há uma lógica sendo executada em segundo plano.
O impacto de ataques CSRF bem-sucedidos pode variar de perdas financeiras e danos à reputação de pessoas e empresas a contas de usuários comprometidas, transações não autorizadas e até responsabilidades legais. Como os ataques CSRF continuam evoluindo e ficando mais sofisticados, desenvolvedores web e organizações precisam implementar contramedidas robustas para proteger a integridade de suas aplicações web.
Neste artigo, vamos explorar como os ataques CSRF funcionam em aplicações Node.js e como se proteger contra eles. Veremos exemplos do mundo real, com etapas práticas e trechos de código, métodos para testar as proteções e boas práticas para proteger aplicações Node.js contra ataques CSRF.
Também oferecemos uma aula prática gratuita sobre CSRF no Snyk Learn para você ir direto ao conteúdo.
Entenda os ataques CSRF
Os ataques CSRF exploram a confiança que as aplicações web depositam nas sessões de usuários autenticados. Ao induzir os usuários a realizar ações sem querer, os atacantes podem manipular ou expor dados confidenciais sem que eles percebam. Antes de aprender a proteger aplicações Node.js contra essas ameaças, vamos revisar como esses ataques funcionam e quais são suas possíveis consequências.
Quando os usuários entram em um site, permanecem conectados por um período que pode variar de dias a meses, até precisarem se autenticar novamente. Esse período em que estão autenticados é chamado de sessão. Durante uma sessão ativa, o servidor cria um identificador ou token exclusivo e aleatório, chamado de ID de sessão, e o associa a essa sessão. Em seguida, o servidor envia o ID de sessão ao usuário, que fica armazenado nos cookies do navegador (dados associados ao navegador do usuário durante uma sessão).
A partir daí, sempre que o usuário faz uma solicitação que altera o estado do site (por exemplo, envia um formulário), o ID de sessão acompanha a solicitação. O servidor compara o ID com o token armazenado no servidor. Se os valores corresponderem, a solicitação é autorizada e a aplicação executa a ação. Caso contrário, o acesso é revogado.
Em um ataque CSRF típico, os atacantes exploram a confiança nas sessões de usuários autenticados criando solicitações maliciosas que imitam uma ação em nome de uma vítima conectada. Criminosos podem realizar ataques CSRF usando técnicas de engenharia social, como inserir links nocivos em e-mails ou sites frequentados pelos usuários visados. Aplicações web desprotegidas podem autorizar essas solicitações inadvertidamente como ações legítimas do usuário, anexando credenciais válidas.
O impacto dos ataques CSRF pode ser grave. Por exemplo, a violação de segurança do Facebook em 2018 afetou mais de 50 milhões de contas no mundo todo. A causa? Tokens de acesso com proteção insuficiente, suscetíveis ao uso indevido entre origens na autenticação de solicitações do lado do cliente. Essa falta de segurança adequada expôs dados confidenciais dos usuários, causando perdas financeiras, danos à reputação e consequências legais. Falhas de segurança podem abalar a confiança dos clientes e prejudicar a reputação de uma organização e de seus serviços.
O impacto de ataques CSRF bem-sucedidos varia conforme a aplicação visada e os recursos que ela disponibiliza aos usuários. Entre as possíveis consequências de um ataque CSRF bem-sucedido estão:
Manipulação de dados — Ataques CSRF podem permitir que os atacantes manipulem dados confidenciais em uma aplicação web. Isso pode incluir alterar as configurações do perfil de um usuário, mudar preferências e configurações da conta e adulterar dados que ele pode modificar dentro dos níveis de acesso permitidos.
Ações não autorizadas — Um atacante pode falsificar solicitações para executar ações que o usuário autenticado não autorizou. Alguns exemplos são enviar formulários, realizar transações financeiras e excluir conteúdo.
Exposição de informações — Os atacantes podem explorar vulnerabilidades e induzir os usuários a revelar dados confidenciais, como mensagens privadas e informações financeiras. Também podem levar os usuários a expor dados proprietários armazenados na aplicação.
Sequestro de conta — Ao induzir os usuários a realizar ações que alteram as credenciais da conta, como mudar os dados de login, os atacantes podem obter acesso não autorizado e assumir o controle das contas comprometidas. Com esse acesso, podem se passar pelo usuário legítimo, acessar recursos restritos e, possivelmente, lançar outros ataques na aplicação ou contra outros usuários.
Estratégias de proteção contra CSRF
As principais técnicas para proteger aplicações Node.js contra ataques CSRF incluem:
Use o padrão de token sincronizador (STP)
O padrão de token sincronizador exige a geração de um token exclusivo para cada sessão de usuário. Ele é incluído nos envios de formulários ou em uma solicitação AJAX, como valor de um cabeçalho personalizado ou parte de uma carga JSON. Ao receber as solicitações, o servidor valida esse token.
O STP gera um token aleatório exclusivo para cada sessão de usuário: o token CSRF. O servidor envia esse token ao usuário como parte da resposta, por exemplo, em uma resposta HTML ou JSON. A aplicação inclui o token nos cabeçalhos da solicitação ou como parâmetro POST personalizado em cada solicitação subsequente. Em seguida, o servidor verifica se o token recebido existe e corresponde ao token da sessão do usuário. Se corresponder, a solicitação foi feita por um usuário legítimo com uma sessão válida.
O STP é simples de implementar e eficaz contra vetores de ataque comuns. No entanto, pode exigir gerenciamento adicional de estado no servidor. Se usar cookies para armazenar tokens CSRF com o STP, adicione o prefixo __Host- para aumentar a segurança dos cookies e impedir o acesso a eles em domínios diferentes daquele em que foram definidos.
Implemente cookies SameSite
A estratégia de cookies SameSite consiste em definir um atributo SameSite nos cookies de sessão para que a aplicação só os envie em solicitações originadas no mesmo domínio dos sites de destino. Esse método impede que as solicitações incluam cookies de outras origens. O atributo SameSite aceita dois valores possíveis: strict e lax.
Quando definido como strict, o navegador só envia cookies de sessão em contextos de primeira parte — eles não são enviados quando o usuário navega para outro site. Esse método impede com eficácia ataques CSRF originados em sites maliciosos de terceiros.
Quando definido como lax, o navegador envia o cookie em solicitações originadas no mesmo site (domínio) que o definiu e em solicitações GET de navegação no nível superior.
Embora os navegadores modernos apliquem automaticamente os atributos SameSite dos cookies, navegadores mais antigos e clientes que não são web, como aplicativos móveis, oferecem suporte limitado. Isso reduz significativamente a eficácia dos cookies SameSite como mecanismo de defesa contra ataques CSRF. Por isso, os desenvolvedores precisam considerar outras estratégias de proteção contra CSRF e garantir uma proteção abrangente em diversos ambientes de cliente.
Use o padrão Double Submit Cookie
O padrão Double Submit Cookie define um cookie adicional com um token exclusivo, além dos identificadores de sessão padrão. Esse cookie é anexado aos cabeçalhos das solicitações do lado do cliente ou aos dados do formulário durante o envio. O padrão Double Submit Cookie reduz a necessidade de gerenciamento de estado sem afetar significativamente o desempenho geral. Por isso, é ideal para aplicações sem estado que usam redes de distribuição de conteúdo diversas ou arquiteturas de microsserviços.
No entanto, esse método não protege contra ataques avançados direcionados a mecanismos de armazenamento do navegador que mantêm tokens secundários. Um desses vetores de ataque é o cross-site scripting (XSS). Aplicações vulneráveis a ataques XSS podem permitir que o atacante extraia o token exclusivo de um cookie e o use em solicitações maliciosas subsequentes.
Além disso, o padrão Double Submit Cookie é vulnerável a ataques man-in-the-middle (MITM) quando os usuários não adotam as medidas de segurança adequadas. O atacante pode interceptar o token CSRF original na solicitação inicial do cliente e usá-lo para criar solicitações maliciosas.
Você pode usar cookies Double Submit assinados para tornar o padrão Double Submit Cookie mais robusto. Essa estratégia usa uma chave secreta conhecida apenas pelo servidor, impedindo que um atacante gere e insira o próprio token CSRF.
Outras medidas de reforço incluem exigir o cabeçalho de resposta HTTP Strict-Transport-Security (HSTS) e usar prefixos de cookie, como __Host-. No entanto, na época da publicação deste artigo, 25% dos navegadores não ofereciam suporte a prefixos de cookie.
Como implementar a proteção contra CSRF em uma aplicação Node.js
Agora que vimos estratégias para proteger nossas aplicações contra ataques CSRF, vamos aprender a implementá-las. Para acompanhar, você precisa ter:
O Node.js instalado no computador.
Um editor de código. Neste guia, usamos o Visual Studio (VS) Code.
Um navegador web
Uma conta gratuita da Snyk, a interface de linha de comando do Snyk Code (CLI) e a extensão do Snyk para VS Code.
Vamos criar uma aplicação simples em Node.js que ajuda os usuários a transferir fundos para outras pessoas. Essa aplicação usará o framework web Express.
Primeiro, crie uma pasta para o projeto chamada nodejs-csrf-strategies. Abra essa pasta em um terminal e execute o comando npm init -y para inicializar um projeto Node.js. Em seguida, execute o comando npm i express para instalar o framework web Express.
Crie um arquivo chamado index.js e cole o código abaixo.
Nesta aplicação de exemplo, a rota / renderiza o formulário HTML para informar o valor a ser transferido. Em seguida, a rota /transfer retorna uma mensagem indicando que o valor informado no formulário foi transferido com sucesso. Em uma aplicação real, a lógica da transferência ficaria aqui. Observe que o código desta aplicação é vulnerável a ataques CSRF — não o use em produção.
Depois de instalar a Snyk CLI no computador, habilite o Snyk Code e analise o projeto de exemplo em busca de vulnerabilidades usando o Snyk Code. A Snyk CLI permite integrar a funcionalidade do Snyk Code ao fluxo de desenvolvimento e executar testes do Snyk Code localmente para verificar se há vulnerabilidades de segurança no código da aplicação.
Para isso, abra a pasta do projeto no terminal. Execute o comando snyk code test. Você verá uma saída semelhante à da captura de tela a seguir, com detalhes sobre transmissão de informações confidenciais em texto simples, uso de hash de senha com esforço computacional insuficiente, exposição de informações, CSRF, negação de serviço por expressão regular (ReDOS) e XSS.

Esse resultado indica que a aplicação está vulnerável a ataques CSRF e aos outros problemas listados.
Passe o cursor sobre a linha que inicializa a aplicação Express — const app = express() — para consultar as informações detalhadas da Snyk sobre ataques CSRF e as boas práticas para evitá-los, como mostra a captura de tela a seguir. Esse recurso de detecção de vulnerabilidades em tempo real é possível graças à extensão do Snyk para VS Code, que ajuda você a descobrir vulnerabilidades enquanto escreve código.

Agora que o Snyk Code identificou a vulnerabilidade a ataques CSRF na nossa aplicação Node.js, vamos ver como as proteções contra CSRF mencionadas podem ajudar.
Protegendo nossa aplicação com o STP
Vamos implementar o STP no app de exemplo em Node.js. Primeiro, instale o middleware csurf com o comando `npm install csurf`. Esse middleware exige que você inicialize um middleware de sessão ou um analisador de cookies. Neste exemplo, vamos usar o analisador de cookies.
Instale um analisador de cookies executando o comando npm install cookie-parser.
Em seguida, importe o middleware e o analisador de cookies no início do arquivo JavaScript.
Ative a análise de cookies e configure o middleware das rotas adicionando o código a seguir logo antes da rota GET.
Precisamos analisar os cookies porque a opção de cookie está definida como true em csrfProtection.
Agora, inclua o token CSRF gerado como um campo de entrada oculto ao exibir o formulário HTML. Isso faz com que o token CSRF seja incluído no formulário HTML enviado. Substitua o código da rota GET pelo código a seguir.
Modifique a rota POST com o código abaixo para aplicar o middleware csrfProtection.
Esse middleware verifica as solicitações POST recebidas comparando o valor do campo _csrf, enviado pela aplicação no corpo da solicitação, com o token CSRF armazenado na sessão do usuário. Esse token está associado aos cookies dele.
Protegendo nosso app com cookies SameSite
Vamos implementar cookies SameSite no app Node.js que criamos anteriormente. Primeiro, instale o middleware cookie-parser executando npm install cookie-parser. Importe o middleware cookie-parser no app e inicialize-o com uma chave secreta usando o código abaixo.
O valor de <your-secret-key> deve ser uma sequência exclusiva de caracteres usada para assinar cookies: um valor aleatório, extenso e imprevisível, gerado por um método criptograficamente seguro.
A assinatura de um cookie com essa sequência exclusiva gera um hash do conteúdo e cria uma assinatura única. Quando o servidor recebe o cookie do navegador mais tarde, pode verificar sua integridade conferindo a assinatura com a mesma chave secreta exclusiva.
Esse método ajuda a evitar o sequestro de sessão, em que um invasor tenta roubar ou se passar pela sessão de um usuário. Se o invasor modificar o cookie, a assinatura deixa de corresponder, e o servidor detecta a adulteração. Assim, os invasores não conseguem modificar os dados relacionados à sessão armazenados nos cookies.
Protegendo nosso app com o padrão Double Submit Cookie
Para implementar o padrão Double Submit Cookie, instale o cookie-parser. Em seguida, adicione o código abaixo ao app para importar os pacotes necessários e ativar a análise de cookies.
Agora, crie funções de middleware para gerar e validar tokens CSRF usando o código abaixo.
Em seguida, aplique o middleware generateCSRFToken à rota GET.
Por fim, aplique a função de middleware validateCSRFToken à rota POST.
Testando a proteção contra CSRF
Testar a proteção contra CSRF é fundamental para:
Impedir ações não autorizadas — Você pode verificar se o app Node.js e as outras estratégias implementadas validam e verificam as solicitações corretamente, impedindo ações não autorizadas.
Proteger os dados e a privacidade dos usuários — Você pode garantir que os dados dos usuários permaneçam seguros e protegidos durante ataques CSRF que tentam expor ou manipular informações confidenciais.
Atender aos padrões de segurança — Muitos padrões e regulamentos de segurança, como o Padrão de Segurança de Dados do Setor de Cartões de Pagamento (PCI DSS), exigem que as organizações implementem proteção contra CSRF. Testar essa proteção ajuda a garantir a conformidade e a evitar penalidades ou problemas legais.
Identificar vulnerabilidades — Ao simular ataques e testar a robustez das medidas de segurança contra CSRF, você pode identificar e corrigir vulnerabilidades antes que pessoas mal-intencionadas as explorem.
Podemos usar um formulário HTML personalizado para simular um ataque CSRF ao código original, sem alterações, do arquivo index.js apresentado no início deste tutorial. Veja o formulário HTML a seguir.
Esse formulário HTML personalizado exibe uma mensagem tentadora para o usuário. Ele informa que a pessoa ganhou um prêmio de US$ 100 e pede que resgate o prêmio clicando no botão. Ao mesmo tempo, um formulário oculto envia o valor ao endpoint /transfer sem que o usuário saiba. O código dentro da tag <script> do formulário HTML envia o formulário automaticamente assim que a página é carregada. No código original, a transferência seria concluída, pois o servidor não conseguiria detectar ameaças de CSRF.
Para testar uma das estratégias de proteção contra CSRF que implementamos, inicie o servidor após aplicar a estratégia STP. Em seguida, tente enviar o formulário HTML personalizado. O servidor exibirá o erro ForbiddenError: invalid csrf token, e a transferência não será processada. Isso mostra que a estratégia de proteção contra CSRF funciona como esperado.
Para testar cada estratégia implementada:
STP — Crie uma solicitação sem o token correto ou com um token expirado. Verifique se o servidor a rejeita.
Cookies SameSite — Faça solicitações de origens cruzadas em diferentes domínios. Verifique se os servidores impedem o acesso não autorizado quando os cookies de sessão estão ausentes.
Double Submit Cookies — Envie uma solicitação com tokens incompatíveis nos dados enviados ou nos cabeçalhos e nos cookies de sessão, e confirme se os servidores fazem a validação corretamente.
Agora, use o Snyk para testar o código atualizado em busca de vulnerabilidades. Você também pode testar o código com outras estratégias, mas, nesta demonstração, vamos nos concentrar no código que implementa a estratégia STP.
Execute o comando snyk code test para verificar se há vulnerabilidades. Você deverá obter um resultado parecido com o da captura de tela a seguir.

Os resultados mostram que o código tem vulnerabilidades de exposição de informações e XSS, mas não de CSRF. Isso significa que corrigimos a vulnerabilidade de CSRF identificada anteriormente.
Boas práticas para proteger apps Node.js contra CSRF
Além de implementar as estratégias acima, reforce a proteção contra CSRF com estas boas práticas:
Atualize regularmente as dependências e os middlewares para manter as configurações de segurança em dia. Ferramentas como o Snyk Open Source simplificam esse processo, identificando componentes desatualizados e sugerindo correções.
Implemente uma Política de Segurança de Conteúdo (CSP) que restrinja as solicitações a fontes confiáveis, reduzindo possíveis vetores de ataque.
Combine várias técnicas de proteção contra CSRF, como STP com cookies SameSite, ou use Double Submit Cookies junto com a aplicação de CSP. Essa abordagem em camadas reduz as vulnerabilidades que podem surgir quando você depende de uma única estratégia.
Próximas etapas
Entender os ataques CSRF e suas implicações é fundamental para proteger apps Node.js. Implementar estratégias robustas de proteção, testá-las com eficácia e seguir as boas práticas reforça as defesas do app contra ameaças.
Testar regularmente a existência de vulnerabilidades — como CSRF — e implementar mecanismos robustos de segurança ajuda a reduzir riscos e impedir ações não autorizadas. Adote medidas proativas de segurança e técnicas atualizadas para criar apps Node.js seguros, corrigindo vulnerabilidades antes que pessoas mal-intencionadas as explorem.
Para proteger seus apps Node.js, aplique os conceitos e as estratégias abordados neste artigo e experimente o Snyk Code.
Comece a participar de desafios de Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.
