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

Crypto no Node.js: Guia Prático

Atualizado em: 31 de julho de 2026

Cadeado ilustrando Crypto no Node.js

Senhas, tokens, assinaturas, webhooks e dados confidenciais exigem operações criptográficas corretas. O módulo Crypto no Node.js, disponível como node:crypto, oferece funções nativas para gerar valores aleatórios, calcular hashes e HMACs, derivar chaves, assinar mensagens e criptografar conteúdo.

Criptografia é uma área em que pequenos erros podem comprometer todo o sistema. Algoritmos antigos, nonces repetidos, chaves em código-fonte e comparações comuns de assinaturas criam vulnerabilidades difíceis de perceber. Por isso, o objetivo não é inventar um protocolo, mas usar primitivas modernas com parâmetros seguros e contratos bem definidos.

Neste guia, você aprenderá hashes, HMAC, randomBytes(), UUIDs, derivação de senha, AES-GCM, assinaturas, comparação em tempo constante, gestão de chaves e testes.

Carregando o módulo crypto

const crypto = require('node:crypto');

A documentação oficial do módulo crypto descreve algoritmos, classes e requisitos de OpenSSL. Para revisar a plataforma, consulte o que é Node.js e o que é JavaScript.

Hash não é criptografia reversível

Uma função hash transforma uma entrada em um resumo de tamanho fixo. Ela é usada para verificar integridade, identificar conteúdo e construir assinaturas, mas não foi feita para recuperar o valor original.

const hash = crypto
  .createHash('sha256')
  .update('conteúdo importante', 'utf8')
  .digest('hex');

console.log(hash);

Alterar um único byte muda o resultado. Para arquivos grandes, alimente o hash progressivamente por stream em vez de carregar tudo na memória.

Hash de arquivo com pipeline

const fs = require('node:fs');

async function hashFile(file) {
  const hash = crypto.createHash('sha256');
  const stream = fs.createReadStream(file);

  for await (const chunk of stream) {
    hash.update(chunk);
  }

  return hash.digest('hex');
}

O guia de Streams no Node.js mostra como processar arquivos grandes com backpressure.

Não use SHA-256 puro para senhas

Hashes rápidos são inadequados para armazenar senhas porque atacantes conseguem testar bilhões de combinações. Use uma função de derivação lenta e com salt, como scrypt, ou uma biblioteca madura com Argon2.

const { promisify } = require('node:util');
const scrypt = promisify(crypto.scrypt);

async function hashPassword(password) {
  const salt = crypto.randomBytes(16);
  const derived = await scrypt(password, salt, 64);

  return {
    salt: salt.toString('base64'),
    hash: derived.toString('base64')
  };
}

Armazene salt, parâmetros e resultado. O salt não precisa ser secreto, mas deve ser único e aleatório por senha.

Verificando uma senha

async function verifyPassword(password, record) {
  const salt = Buffer.from(record.salt, 'base64');
  const expected = Buffer.from(record.hash, 'base64');
  const actual = await scrypt(password, salt, expected.length);

  return crypto.timingSafeEqual(expected, actual);
}

Use comparação em tempo constante quando os Buffers têm o mesmo tamanho. O artigo sobre Buffer no Node.js explica codificações e memória binária.

Gerando tokens aleatórios

const token = crypto.randomBytes(32).toString('base64url');

Trinta e dois bytes oferecem ampla entropia para tokens de redefinição e sessões. Não use Math.random() para segredos, pois ele não é um gerador criptograficamente seguro.

Armazene no banco apenas o hash do token quando possível. Se o banco vazar, o valor original enviado ao usuário não estará disponível diretamente.

UUIDs

const id = crypto.randomUUID();

UUID é adequado para identificadores não sequenciais, mas não substitui automaticamente um token secreto em todos os cenários. Avalie a entropia e a finalidade.

HMAC para autenticidade

HMAC combina uma chave secreta com a mensagem. Ele permite verificar se o conteúdo foi alterado e se veio de alguém que conhece a chave.

function signPayload(payload, secret) {
  return crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
}

Na validação, calcule novamente sobre os bytes exatos e use comparação segura:

function verifyHmac(payload, signature, secret) {
  const expected = Buffer.from(
    signPayload(payload, secret),
    'hex'
  );
  const received = Buffer.from(signature, 'hex');

  if (expected.length !== received.length) return false;
  return crypto.timingSafeEqual(expected, received);
}

Esse padrão é usado em webhooks seguros com Node.js.

Criptografia autenticada com AES-GCM

AES-GCM protege confidencialidade e integridade. Cada criptografia precisa de um IV único para a mesma chave.

function encrypt(text, key) {
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv(
    'aes-256-gcm',
    key,
    iv
  );

  const encrypted = Buffer.concat([
    cipher.update(text, 'utf8'),
    cipher.final()
  ]);

  return {
    iv: iv.toString('base64'),
    data: encrypted.toString('base64'),
    tag: cipher.getAuthTag().toString('base64')
  };
}

A chave deve possuir 32 bytes para AES-256. O IV não é secreto, mas nunca deve ser repetido com a mesma chave.

Descriptografando AES-GCM

function decrypt(record, key) {
  const decipher = crypto.createDecipheriv(
    'aes-256-gcm',
    key,
    Buffer.from(record.iv, 'base64')
  );

  decipher.setAuthTag(
    Buffer.from(record.tag, 'base64')
  );

  const plain = Buffer.concat([
    decipher.update(Buffer.from(record.data, 'base64')),
    decipher.final()
  ]);

  return plain.toString('utf8');
}

Se o conteúdo, IV ou tag forem alterados, final() falhará. Não ignore essa exceção.

Dados adicionais autenticados

GCM pode autenticar metadados que não precisam ser criptografados, como versão e identificador:

const aad = Buffer.from(`user:${userId}`);
cipher.setAAD(aad);

Na descriptografia, forneça exatamente os mesmos bytes antes de validar a tag.

Chaves derivadas de senha

Não use uma senha diretamente como chave AES. Derive uma chave com salt e parâmetros adequados:

async function deriveKey(password, salt) {
  return scrypt(password, salt, 32);
}

Para dados persistentes, registre versão e parâmetros de derivação, permitindo aumentar o custo no futuro.

Assinaturas digitais

Assinaturas assimétricas usam chave privada para assinar e chave pública para verificar:

const signature = crypto.sign(
  null,
  Buffer.from(message),
  privateKey
);

const valid = crypto.verify(
  null,
  Buffer.from(message),
  publicKey,
  signature
);

O algoritmo depende do tipo da chave. Ed25519 é uma opção moderna e simples quando compatível com o ecossistema.

Gerando um par de chaves

const { publicKey, privateKey } =
  crypto.generateKeyPairSync('ed25519');

Em servidores, prefira a versão assíncrona quando a geração puder ser custosa. Exporte chaves em formatos padronizados e proteja a privada com controle de acesso ou serviço de gestão de chaves.

Web Crypto API

Node.js também oferece crypto.webcrypto e o objeto global crypto.subtle em versões atuais. A API segue padrões da Web e usa Promises. Escolha um estilo consistente e confirme suporte na versão mínima do projeto.

A documentação oficial de Web Crypto detalha algoritmos e formatos aceitos.

Gestão de chaves

Não coloque chaves em repositórios, imagens Docker ou logs. Use variáveis protegidas, arquivos montados com permissões restritas, secret managers ou KMS. Separe chaves por ambiente e finalidade.

Planeje rotação. Um registro criptografado deve indicar qual versão de chave foi usada. Durante uma transição, a aplicação pode descriptografar com versões antigas e gravar novos dados com a atual.

Não reutilize chaves para tudo

Use chaves diferentes para criptografia, HMAC e outros contextos. A separação limita o impacto de um vazamento e evita interações entre protocolos.

Evite algoritmos obsoletos

Não adote MD5 ou SHA-1 para segurança. Evite modos sem autenticação, como ECB, e não invente combinações de cifra, hash e padding. Algoritmos podem ser desabilitados conforme a configuração do OpenSSL.

Criptografia e desempenho

Derivação de senha e geração de chaves consomem CPU. Limite concorrência e evite parâmetros tão altos que permitam negação de serviço. Para tarefas pesadas, monitore o event loop e avalie Worker Threads em casos medidos.

Tratamento de erros

Não devolva detalhes internos de OpenSSL ao cliente. Registre uma categoria segura, um identificador de correlação e o contexto necessário. Diferencie dados inválidos de falhas de configuração.

Como testar

Use vetores conhecidos e confirme que:

  • hashes e HMACs possuem resultados esperados;
  • criptografar e descriptografar recupera o texto;
  • alterar um byte invalida a tag;
  • IVs são diferentes em operações sucessivas;
  • chave incorreta falha;
  • tokens têm tamanho e formato definidos;
  • rotação de chave mantém compatibilidade.

Erros comuns

  • Usar Math.random para tokens: valores podem ser previsíveis.
  • Usar hash rápido para senha: ataques de força bruta ficam baratos.
  • Repetir IV no GCM: a segurança da chave é comprometida.
  • Confundir Base64 com proteção: conteúdo é facilmente reversível.
  • Comparar assinatura com ===: diferenças de tempo podem vazar informação.
  • Guardar chave no código: o segredo se espalha pelo histórico e artefatos.
  • Não planejar rotação: trocar chave quebra dados existentes.
  • Inventar protocolo: detalhes não revisados criam vulnerabilidades.

Boas práticas para produção

  • Use primitivas modernas e documentadas.
  • Gere tokens com randomBytes().
  • Derive senhas com scrypt ou Argon2.
  • Use criptografia autenticada, como AES-GCM.
  • Nunca repita nonce ou IV com a mesma chave.
  • Compare segredos em tempo constante.
  • Separe chaves por finalidade e ambiente.
  • Implemente versionamento e rotação.
  • Não registre chaves, tokens ou texto confidencial.
  • Teste corrupção e parâmetros inválidos.

Conclusão

O Crypto no Node.js oferece recursos sólidos para hashes, HMACs, tokens, derivação de chaves, criptografia e assinaturas. Essas APIs evitam a necessidade de implementar algoritmos manualmente e se integram bem a Buffers e streams.

Segurança depende do uso correto. Escolha algoritmos modernos, mantenha chaves fora do código, garanta IVs únicos e trate autenticação como parte obrigatória da criptografia. Com gestão de segredos e testes, o módulo pode proteger dados e integrações de maneira confiável.

Os 10 Melhores Cursos de Programação de 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