Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

CSRF no Node.js

Atualizado em: 5 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

CSRF, ou Cross-Site Request Forgery, acontece quando um site malicioso induz o navegador de uma vítima autenticada a enviar uma requisição para outra aplicação. O ataque explora credenciais enviadas automaticamente, principalmente cookies de sessão.

CORS não elimina CSRF. O navegador pode bloquear a leitura da resposta e ainda assim enviar formulários ou requisições simples. A proteção precisa combinar cookies adequados, tokens, validação de origem e desenho seguro das rotas.

Quando a aplicação está exposta

O risco é maior quando:

  • a autenticação usa cookies;
  • rotas alteram estado com POST, PUT, PATCH ou DELETE;
  • GET executa ações;
  • cookies permitem contexto cross-site;
  • não há token CSRF;
  • a origem não é validada;
  • sessões permanecem ativas por muito tempo.

GET deve ser seguro

Não use GET para excluir, confirmar, pagar, alterar senha ou executar ações. Crawlers, previews e links externos podem disparar GET automaticamente.

// Evite
app.get('/account/delete', deleteAccount);

// Prefira
app.delete('/account', requireAuth, requireCsrf, deleteAccount);

SameSite

Cookies com SameSite=Lax ou Strict reduzem o envio em contextos cross-site:

res.cookie('session', sessionId, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  path: '/',
});

Strict é mais restritivo, mas pode prejudicar fluxos legítimos iniciados por links externos. None permite cross-site e exige Secure; nesse caso, uma proteção CSRF explícita torna-se essencial.

Synchronizer token

O servidor gera um token aleatório ligado à sessão. O frontend envia o token em um campo ou header:

import crypto from 'node:crypto';

function createCsrfToken(req) {
  const token = crypto.randomBytes(32).toString('base64url');
  req.session.csrfToken = token;
  return token;
}

Validação:

function requireCsrf(req, res, next) {
  const received = req.get('x-csrf-token');
  const expected = req.session.csrfToken;

  if (!received || !expected) {
    res.status(403).json({ error: 'csrf_validation_failed' });
    return;
  }

  const a = Buffer.from(received);
  const b = Buffer.from(expected);

  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    res.status(403).json({ error: 'csrf_validation_failed' });
    return;
  }

  next();
}

Compare valores de tamanho igual e não registre o token.

O servidor envia um cookie CSRF legível pelo JavaScript e o cliente repete o valor em um header. O servidor compara os dois.

Para reduzir substituição do cookie por subdomínios ou atacantes, assine o valor e vincule-o à sessão. Não use um valor previsível.

Validação de Origin

const allowedOrigins = new Set([
  'https://app.example.com',
]);

function validateOrigin(req, res, next) {
  const origin = req.get('origin');

  if (!origin || !allowedOrigins.has(origin)) {
    res.status(403).json({ error: 'origin_not_allowed' });
    return;
  }

  next();
}

Para requisições antigas sem Origin, o Referer pode ser usado como fallback, com parsing completo da URL. Não compare apenas prefixos de string.

Headers personalizados

Exigir X-CSRF-Token faz o navegador enviar preflight em chamadas cross-origin. Isso acrescenta uma barreira, mas não substitui o token e a validação de origem.

Tokens por sessão ou por requisição

Token por sessão é mais simples. Token rotativo por requisição reduz reutilização, mas complica abas paralelas, retries e navegação. Escolha conforme risco e usabilidade.

Rotação de sessão

Ao autenticar, elevar privilégio ou trocar senha, regenere o ID da sessão e considere renovar o token CSRF. Isso reduz session fixation e uso de contexto antigo.

APIs com Bearer token

Quando o token é enviado manualmente no header Authorization e não fica em cookie, o navegador não o anexa automaticamente, reduzindo o CSRF clássico. Porém, XSS pode roubar o token. A arquitetura precisa proteger ambos os vetores.

Cookies de refresh token

Uma aplicação pode manter access token em memória e refresh token em cookie HttpOnly. A rota de refresh ainda recebe cookie automaticamente e precisa de SameSite, validação de origem e, conforme a arquitetura, token CSRF.

Formulários HTML

<form method="post" action="/profile">
  <input type="hidden" name="_csrf" value="{{csrfToken}}">
  <button type="submit">Salvar</button>
</form>

Valide o token antes da lógica de negócio. Limite tamanho do body e Content-Type.

GraphQL

GraphQL costuma usar POST para queries e mutations. Não trate todo POST como alteração, mas aplique proteção à operação autenticada. Bloqueie GET para mutations e limite Content-Type e operações persistidas quando adequado.

Webhooks não usam sessão de navegador

Webhooks devem usar assinatura, timestamp, proteção contra replay e segredo compartilhado ou mTLS. Token CSRF não é o mecanismo correto.

Login CSRF

Um atacante pode forçar a vítima a entrar na conta controlada pelo atacante, fazendo ações posteriores serem registradas na conta errada. A rota de login também pode exigir token e validação de origem.

Logout CSRF

Forçar logout parece menos grave, mas pode ser usado para interrupção ou confusão. Use POST com proteção para logout e não um link GET.

Subdomínios

Cookies com Domain=.example.com são enviados a vários subdomínios. Um subdomínio comprometido pode ampliar o risco. Prefira cookies host-only e prefixos como __Host- quando possível.

Falhas e resposta

Retorne 403 com código estável. Não informe se o token existia, expirou ou divergiu. Registre apenas contexto seguro: rota, origem, método, request ID e motivo categorizado.

Rate limiting

Falhas CSRF repetidas podem indicar automação, frontend antigo ou ataque. Aplique rate limiting e alertas sem bloquear usuários legítimos por uma única falha.

Testes

await request(app)
  .post('/profile')
  .set('Cookie', sessionCookie)
  .expect(403);

await request(app)
  .post('/profile')
  .set('Cookie', sessionCookie)
  .set('Origin', 'https://app.example.com')
  .set('X-CSRF-Token', csrfToken)
  .expect(200);

Teste token ausente, incorreto, de outra sessão, origem não permitida, GET indevido e cookie cross-site.

Erros comuns

  • confiar apenas em CORS;
  • usar GET para alterações;
  • colocar token CSRF em cookie HttpOnly sem outra forma de leitura;
  • aceitar qualquer Origin;
  • usar token previsível;
  • não vincular o token à sessão;
  • registrar tokens;
  • ignorar login e refresh;
  • configurar SameSite=None sem proteção.

Fluxo recomendado

  1. torne GET seguro;
  2. configure cookies Secure, HttpOnly e SameSite;
  3. gere token aleatório;
  4. vincule à sessão;
  5. exija header ou campo;
  6. valide Origin;
  7. proteja login, logout e refresh;
  8. teste fluxos cross-site;
  9. monitore falhas.

Combine a proteção com CORS no Node.js, Helmet, Rate Limiting e HTTPS.

Consulte a referência da OWASP sobre CSRF e a documentação de cookies da MDN.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita