O Gerador De CPF O Gerador De CPF

Como Armazenar CPF com Segurança no Banco de Dados

O CPF é dado pessoal segundo a LGPD e merece proteção adequada no banco de dados. Porém, o keyspace pequeno do CPF (~10⁹ valores válidos) muda completamente a estratégia em relação a senhas. Técnicas que funcionam bem para senhas — como bcrypt e Argon2 — não são adequadas para CPFs.

O problema do keyspace

Uma senha forte tem entropia alta: bilhões de bilhões de combinações possíveis. O CPF, não:

  • Formato: 11 dígitos numéricos (000.000.000-00 a 999.999.999-99)
  • Valores válidos: ~1,2 bilhão (descartando dígitos verificadores inválidos)
  • SHA-256 de todos os CPFs possíveis: calculável em minutos num computador comum

Isso significa que um hash sem segredo é efetivamente texto plano. Um atacante com acesso ao banco pode gerar a rainbow table completa em poucos minutos e reverter todos os hashes.

Técnicas e quando usar cada uma

TécnicaReversívelBuscaSeguro para CPFQuando usar
SHA-256NãoSimNãoNunca para CPF
SHA-256 + saltNãoNãoNãoNunca para CPF
bcrypt / Argon2NãoNãoNãoNunca para CPF
HMAC-SHA256 + pepperNãoSimSimVerificação / dedup
AES-256-GCMSimNãoSimExibição / exportação
TokenizaçãoSim (token)SimSistemas distribuídos

Por que NÃO usar bcrypt/Argon2 para CPF

bcrypt e Argon2 foram projetados para senhas, onde a entropia é alta e o custo computacional por tentativa compensa. Para CPFs, o cenário é diferente:

  1. Salt impede busca: cada registro tem um salt diferente, tornando impossível buscar por WHERE hash = ?. Inviável para lookup.
  2. Keyspace pequeno: mesmo com 100 ms por hash (bcrypt cost 12), o total de 1,2 × 10⁹ hashes levaria ~3,8 anos em 1 CPU. Parece muito, mas:
    • GPUs modernas paralelizam bcrypt com eficiência moderada
    • Um cluster de GPUs reduz isso para horas ou dias
    • O atacante precisa fazer o ataque apenas uma vez para montar a tabela
  3. Custo não compensa: o custo computacional do bcrypt/Argon2 é projetado para senhas com entropia de 40+ bits. Para ~30 bits de entropia (CPF), a proteção é insuficiente.

HMAC-SHA256 com pepper

O HMAC resolve o problema do keyspace adicionando uma chave secreta (pepper) ao cálculo. Sem o pepper, o atacante não consegue computar os hashes mesmo com acesso total ao banco.

Quando usar: verificação de existência, deduplicação, busca por CPF.

import { createHmac } from "node:crypto";

// Pepper: chave secreta armazenada fora do banco (env var, KMS)
const PEPPER = process.env.CPF_HMAC_KEY; // 256 bits, hex

function hmacCpf(cpf) {
  const digits = cpf.replace(/\D/g, "");
  return createHmac("sha256", Buffer.from(PEPPER, "hex"))
    .update(digits)
    .digest("hex");
}

// Armazenar: INSERT INTO users (cpf_hmac) VALUES ($1)
// Buscar:    SELECT * FROM users WHERE cpf_hmac = $1

O pepper funciona como um segredo compartilhado: se o banco vaza, o atacante tem os HMACs mas não consegue revertê-los sem a chave. Diferente de um salt, o pepper é o mesmo para todos os registros, o que permite busca exata por WHERE cpf_hmac = ?.

AES-256-GCM para armazenamento reversível

Quando você precisa exibir, exportar ou enviar o CPF, o hash não serve — você precisa de criptografia reversível. AES-256-GCM é a escolha padrão:

  • AES-256: cifra simétrica de 256 bits
  • GCM: modo autenticado (detecta adulteração do dado cifrado)
  • IV: vetor de inicialização único por registro (impede que dois CPFs iguais gerem o mesmo ciphertext)
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";

const KEY = Buffer.from(process.env.CPF_ENCRYPTION_KEY, "hex"); // 32 bytes

function encrypt(cpf) {
  const digits = cpf.replace(/\D/g, "");
  const iv = randomBytes(12); // 96 bits para GCM
  const cipher = createCipheriv("aes-256-gcm", KEY, iv);

  let encrypted = cipher.update(digits, "utf8", "hex");
  encrypted += cipher.final("hex");
  const tag = cipher.getAuthTag().toString("hex");

  return { encrypted, iv: iv.toString("hex"), tag };
}

function decrypt(encrypted, ivHex, tagHex) {
  const iv = Buffer.from(ivHex, "hex");
  const decipher = createDecipheriv("aes-256-gcm", KEY, iv);
  decipher.setAuthTag(Buffer.from(tagHex, "hex"));

  let decrypted = decipher.update(encrypted, "hex", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
}

Combinando as duas técnicas

O padrão recomendado usa duas colunas: uma para busca (HMAC) e outra para recuperação (AES):

CREATE TABLE users (
  id          SERIAL PRIMARY KEY,
  cpf_hmac    CHAR(64) NOT NULL,   -- HMAC-SHA256 para busca
  cpf_enc     TEXT NOT NULL,        -- AES-256-GCM cifrado
  cpf_iv      CHAR(24) NOT NULL,    -- IV em hex
  cpf_tag     CHAR(32) NOT NULL     -- Auth tag em hex
);

CREATE UNIQUE INDEX idx_cpf_hmac ON users (cpf_hmac);

Funções de armazenamento e busca:

function storeCpf(cpf) {
  const hmac = hmacCpf(cpf);
  const { encrypted, iv, tag } = encrypt(cpf);
  return { cpf_hmac: hmac, cpf_enc: encrypted, cpf_iv: iv, cpf_tag: tag };
}

function searchCpf(cpf) {
  const hmac = hmacCpf(cpf);
  // SELECT * FROM users WHERE cpf_hmac = $1
  return hmac;
}

function retrieveCpf(row) {
  return decrypt(row.cpf_enc, row.cpf_iv, row.cpf_tag);
}

Gerenciamento de chaves

As chaves de HMAC e criptografia são a parte mais crítica da arquitetura. Se o atacante obtiver as chaves, todas as proteções caem:

  • Nunca no código-fonte: chaves hardcoded vão para o histórico do Git e ficam expostas
  • Variáveis de ambiente: nível mínimo aceitável (CPF_HMAC_KEY, CPF_ENCRYPTION_KEY)
  • KMS em produção: AWS KMS, GCP Cloud KMS ou Azure Key Vault — o serviço gerencia rotação, auditoria e controle de acesso
  • Rotação de chaves: re-encrypt periódico dos dados com a nova chave; mantenha a chave antiga ativa temporariamente para leitura

LGPD e CPF

A Lei Geral de Proteção de Dados (Lei nº 13.709/2018) classifica o CPF como dado pessoal:

  • Base legal: você precisa de uma base legal válida para processar CPFs (consentimento, execução de contrato, obrigação legal, etc.)
  • Minimização: não armazene o CPF se não precisa dele — pergunte se o dado é realmente necessário
  • Direito à exclusão: o titular pode solicitar a exclusão do seu CPF — sua arquitetura deve suportar isso
  • Segurança: a LGPD exige medidas técnicas adequadas — criptografia e HMAC atendem a esse requisito

Antes de armazenar, é fundamental validar o CPF no momento do cadastro para garantir que apenas números com dígitos verificadores corretos cheguem ao banco. Para ambientes de desenvolvimento e homologação, use o gerador de CPF para popular o banco com dados de teste sem risco de usar dados reais.

Perguntas frequentes sobre armazenamento de CPF

Posso usar bcrypt ou Argon2 para armazenar CPF?
Não é recomendado. bcrypt e Argon2 foram projetados para senhas, que possuem entropia alta. O CPF tem apenas ~1,2 bilhão de valores possíveis. Mesmo com o custo computacional do bcrypt, um cluster de GPUs consegue gerar a tabela completa em horas. Além disso, o salt por registro impede buscas com WHERE, o que inviabiliza consultas por CPF.
Qual a diferença entre HMAC e um hash simples como SHA-256?
O SHA-256 sem segredo é vulnerável a rainbow tables: um atacante gera o hash de todos os 1,2 bilhão de CPFs válidos em minutos e reverte qualquer hash do banco. O HMAC adiciona uma chave secreta (pepper) ao cálculo. Sem essa chave, o atacante não consegue computar os hashes mesmo com acesso total ao banco de dados.
Preciso usar HMAC e AES juntos?
Depende do caso de uso. Se você só precisa verificar se um CPF já existe no banco (deduplicação, login), o HMAC é suficiente. Se precisa exibir o CPF para o usuário ou exportá-lo, precisa de AES para recuperar o número original. O padrão mais completo usa duas colunas: HMAC para busca e AES para recuperação.
Onde devo guardar as chaves de criptografia?
Nunca no código-fonte ou no repositório Git. O mínimo aceitável são variáveis de ambiente. Em produção, use um serviço de gerenciamento de chaves como AWS KMS, GCP Cloud KMS ou Azure Key Vault, que oferecem rotação automática, auditoria de acesso e controle de permissões.
Guardar CPF em texto plano viola a LGPD?
A LGPD exige que o controlador adote medidas técnicas e administrativas para proteger dados pessoais contra acessos não autorizados. Armazenar CPF em texto plano em bancos de dados, logs ou arquivos de configuração não atende a esse requisito. Em caso de incidente, a ausência de criptografia pode ser considerada negligência pela ANPD.
O que é tokenização de CPF?
Tokenização substitui o CPF por um identificador aleatório (token) sem relação matemática com o número original. O mapeamento token-CPF fica em um cofre separado com acesso restrito. É útil em sistemas distribuídos onde vários serviços precisam referenciar o mesmo usuário sem que cada um tenha acesso ao CPF real.