Documentos brasileiros para devs: formatos e algoritmos de va...
Documentos brasileiros para devs: formatos e algoritmos de validação
Cada documento de identidade brasileiro tem estrutura própria, entidade emissora distinta e, na maioria dos casos, um algoritmo de dígito verificador diferente. Implementar validação correta exige conhecer cada um desses detalhes antes de escrever uma linha de código.
Cada documento tem estrutura própria e algoritmo distinto
Por que validação por checksum existe (e por que muitos devs erram)
O dígito verificador (DV) detecta erros de digitação: transposição de algarismos, dígito faltante, número inventado. Ele não confirma que o documento foi emitido nem que a pessoa existe — apenas que os números respeitam o cálculo definido pelo órgão emissor.
O erro mais comum é confundir "passa no checksum" com "documento válido". CPFs zerados (000.000.000-00) e CNPJs com todos os dígitos iguais passam em implementações ingênuas porque o algoritmo não descarta essas sequências por padrão.
Tabela comparativa: tamanho, formato, dígito verificador, entidade emissora
| Documento | Tamanho | Formato | DVs | Emitente |
|---|---|---|---|---|
| CPF | 11 dígitos | NNN.NNN.NNN-DD | 2 | Receita Federal |
| CNPJ | 14 chars | NN.NNN.NNN/EEEE-DD | 2 | Receita Federal |
| RG | variável | por estado | 0 ou 1 | SSP / DETRAN estadual |
| CIN | 12 chars | UF + 9 dígitos + D | 1 | MJ / SENASP |
| CNH | 11 dígitos | sem máscara pública | 2 | SENATRAN / DETRAN |
---
CPF: onze dígitos e dois módulos 11
Anatomia do número (região fiscal, sequencial, DVs)
O CPF tem 11 dígitos: 9 de base e 2 verificadores. Os três primeiros identificam a região fiscal de inscrição (01-09 = Brasília, 10 = SP). Os dígitos 1-9 formam o número de inscrição; os dígitos 10 e 11 são os DVs calculados em duas rodadas distintas.
Algoritmo passo a passo com pesos [10..2] e [11..2]
Primeiro DV: multiplica os 9 primeiros dígitos pelos pesos 10, 9, 8, 7, 6, 5, 4, 3, 2. Soma os produtos. Calcula resto = soma % 11. Se resto < 2, DV = 0; caso contrário, DV = 11 - resto.
Segundo DV: usa os 9 dígitos originais mais o primeiro DV, com pesos 11, 10, 9, 8, 7, 6, 5, 4, 3, 2. Mesma regra de resto.
Casos especiais que passam no checksum mas são inválidos (111.111.111-11)
Sequências com todos os dígitos iguais (000.000.000-00 até 999.999.999-99) geram DVs matematicamente coerentes, mas a Receita Federal nunca as emite. Qualquer validação deve rejeitar esses onze padrões explicitamente.
Implementação TypeScript completa com casos de teste
function normalizarDocumento(doc: string): string {
return doc.trim().toUpperCase().replace(/[^A-Z0-9]/g, '');
}
function validarCPF(cpf: string): boolean {
const s = normalizarDocumento(cpf);
if (s.length !== 11 || /^(\d)\1{10}$/.test(s)) return false;
const calcDv = (base: number[]) => {
const len = base.length;
const soma = base.reduce((acc, d, i) => acc + d * (len + 1 - i), 0);
const r = soma % 11;
return r < 2 ? 0 : 11 - r;
};
const digits = s.split('').map(Number);
const dv1 = calcDv(digits.slice(0, 9));
if (dv1 !== digits[9]) return false;
const dv2 = calcDv(digits.slice(0, 10));
return dv2 === digits[10];
}
console.assert(validarCPF('529.982.247-25') === true);
console.assert(validarCPF('111.111.111-11') === false);
console.assert(validarCPF('000.000.000-00') === false);Use o validador de CPF online para conferir casos limítrofes antes de subir para produção. Para gerar CPFs válidos para testes, o gerador calcula os dois DVs pelo mesmo algoritmo acima.
---
CNPJ numérico: quatorze dígitos e pesos cíclicos
Estrutura: raiz, ordem, estabelecimento, DVs
O CNPJ tem 14 posições: 8 de raiz (identifica a empresa), 4 de ordem de filial (0001 = matriz) e 2 DVs. Formato mascarado: NN.NNN.NNN/EEEE-DD.
Pesos [5,4,3,2,9,8,7,6,5,4,3,2] e segundo ciclo
Os 12 primeiros caracteres são multiplicados pelos pesos [5,4,3,2,9,8,7,6,5,4,3,2] para o primeiro DV. Para o segundo, os pesos são [6,5,4,3,2,9,8,7,6,5,4,3,2], incluindo o DV anterior. A regra de resto é idêntica à do CPF.
Implementação TypeScript e diferenças para o CPF
function validarCNPJ(cnpj: string): boolean {
const s = normalizarDocumento(cnpj);
if (s.length !== 14 || /^(.)\1{13}$/.test(s)) return false;
const pesos1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
const pesos2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
const calcDv = (chars: string[], pesos: number[]) => {
const soma = chars.reduce((acc, c, i) => {
const v = c >= 'A' ? c.charCodeAt(0) - 55 : Number(c);
return acc + v * pesos[i];
}, 0);
const r = soma % 11;
return r < 2 ? 0 : 11 - r;
};
const chars = s.split('');
const dv1 = calcDv(chars.slice(0, 12), pesos1);
if (dv1 !== Number(chars[12])) return false;
const dv2 = calcDv(chars.slice(0, 13), pesos2);
return dv2 === Number(chars[13]);
}Essa implementação já suporta letras na raiz. Use /gerador-cnpj para CNPJs numéricos em fixtures e /validar-cnpj para verificação online.
---
CNPJ alfanumérico: vigência 01/07/2026 (Receita Federal)
O que muda na estrutura (raiz aceita A-Z0-9, ordem permanece numérica)
A partir de 01/07/2026, a Receita Federal passa a emitir CNPJs com letras nos 8 dígitos da raiz (Instrução Normativa RFB 2.229/2024). A parte de ordem (posições 9-12) permanece numérica. Os dois DVs continuam numéricos. Formato futuro: B3.QK4.L7/0001-XX.
Como o checksum se adapta: valor ASCII de letras, mesmo módulo 11
Letras são convertidas pelo valor posicional: A = 10, B = 11, Z = 35, equivalente a charCodeAt(0) - 55 para maiúsculas. O módulo 11 não muda. A função validarCNPJ acima já contempla isso.
Impacto em sistemas legados: regex, máscaras, banco de dados (VARCHAR vs CHAR)
Três pontos críticos de migração:
1. Regex que assume \d{14} rejeita o novo formato. Substituir por [A-Z0-9]{14} após normalização para maiúsculas. 2. Máscara de input baseada em replace(/\D/g, '') descarta letras. Ajustar para replace(/[^A-Z0-9]/gi, ''). 3. Coluna de banco tipada como CHAR(14) sem máscara funciona para ambos os formatos, pois letras ASCII também ocupam 1 byte.
AVISO: Não armazene CNPJ com máscara (pontos, barra, hífen) no banco. Isso quebra índices, duplica espaço e complica queries de JOIN. Normalize antes de persistir.
Snippet de migração para aceitar os dois formatos em paralelo
function normalizarCNPJ(cnpj: string): string {
return cnpj.toUpperCase().replace(/[^A-Z0-9]/g, '');
}
function isCNPJAlfanumerico(cnpj: string): boolean {
return /[A-Z]/.test(normalizarCNPJ(cnpj));
}Para gerar CNPJs no novo formato, use /gerador-cnpj-alfanumerico.
---
RG: sem padrão nacional, mas com padrões estaduais
Mapeamento por estado (SSP-SP, SSP-RJ, SSP-MG, DETRAN-RS)
| UF | Formato típico | DV | Emitente |
|---|---|---|---|
| SP | NN.NNN.NNN-D (ou X) | mod 11 | SSP-SP |
| RJ | NN.NNN.NNN-D | mod 11 | DETRAN-RJ |
| MG | M-NNN.NNN | sem DV público | SSP-MG |
| RS | NNN.NNNNN | sem DV público | SSP-RS |
SSP-SP: dígito verificador com módulo 11 e letra X
O RG de SP tem 8 algarismos base e 1 DV. Pesos de 2 a 9 são aplicados da direita para a esquerda. O DV pode ser numérico ou a letra X quando o resultado do módulo for 10.
Por que validar RG é diferente: você valida formato, não checksum universal
Não existe algoritmo federal para RG. A validação cross-state resume-se a: comprimento dentro do intervalo esperado (6-10 caracteres), caracteres permitidos (dígitos e, em SP, X na última posição) e máscara compatível com a UF informada pelo usuário.
Estratégia prática: aceitar, mascarar e delegar conferência ao emitente
Para formulários, aceite o número informado, aplique a máscara da UF selecionada e nunca reprove por checksum — apenas por caracteres inválidos. Use /gerador-rg para gerar RGs no formato SSP-SP em fixtures de teste.
---
CIN: o novo documento de identidade nacional (EC 131/2023)
Estrutura definida pelo MJ: letras UF + 9 dígitos + DV
A Emenda Constitucional 131/2023 instituiu o CIN (Carteira de Identidade Nacional). O número segue o padrão UF (2 letras) + 9 dígitos + 1 DV, totalizando 12 caracteres. Exemplo: SP123456789-4.
Relação com RG: CIN não substitui, coexiste até 2032
O RG permanece válido até 28/02/2032. Sistemas que coletam documentos de identidade devem aceitar ambos. Colunas separadas (cin CHAR(12) NULL e rg VARCHAR(20) NULL) são mais simples de migrar do que um campo genérico documento_identidade.
Algoritmo de dígito verificador do CIN
O DV é calculado sobre os 9 dígitos centrais com pesos de 2 a 10, da esquerda para a direita. Soma dos produtos, módulo 11. Se resto < 2, DV = 0; caso contrário, DV = 11 - resto. A sigla da UF não entra no cálculo.
function validarCIN(cin: string): boolean {
const s = cin.toUpperCase().replace(/[^A-Z0-9]/g, '');
if (!/^[A-Z]{2}\d{10}$/.test(s)) return false;
const digits = s.slice(2).split('').map(Number);
const soma = digits.slice(0, 9).reduce((acc, d, i) => acc + d * (i + 2), 0);
const r = soma % 11;
const dv = r < 2 ? 0 : 11 - r;
return dv === digits[9];
}Use /gerador-cin para gerar CINs válidos em seeds de banco de dados.
---
CNH: onze dígitos, dois DVs calculados em sequências distintas
Estrutura do Registro Nacional de Condutores Habilitados (RENACH/SENATRAN)
A CNH tem 11 dígitos no campo RENACH. Os 9 primeiros são o número base; os dígitos 10 e 11 são DVs calculados em duas etapas com lógicas distintas entre si.
Cálculo do primeiro DV: pesos decrescentes 9..1
Multiplica os 9 dígitos pelos pesos 9, 8, 7, 6, 5, 4, 3, 2, 1. Soma. Divide por 11. O DV1 é o resto. Se resto = 10, guarda um flag dsc = 2; caso contrário, dsc = 0.
Cálculo do segundo DV: pesos crescentes 1..9 com DSC condicional
Multiplica os mesmos 9 dígitos pelos pesos 1, 2, 3, 4, 5, 6, 7, 8, 9. Soma. Divide por 11. DV2 = resto. Se DV2 >= 10, DV2 = 0. DV1 definitivo = DV1 - dsc; se negativo, DV1 = 0.
Implementação TypeScript com os dois passos encadeados
function validarCNH(cnh: string): boolean {
const s = normalizarDocumento(cnh);
if (s.length !== 11 || /^(\d)\1{10}$/.test(s)) return false;
const d = s.split('').map(Number);
let soma1 = 0;
let dsc = 0;
for (let i = 0; i < 9; i++) soma1 += d[i] * (9 - i);
let dv1 = soma1 % 11;
if (dv1 >= 10) { dv1 = 0; dsc = 2; }
let soma2 = 0;
for (let i = 0; i < 9; i++) soma2 += d[i] * (1 + i);
let dv2 = soma2 % 11;
if (dv2 >= 10) dv2 = 0;
dv1 = dv1 - dsc < 0 ? 0 : dv1 - dsc;
return d[9] === dv1 && d[10] === dv2;
}Para gerar CNHs com DV correto em massa, a API REST aceita ?type=cnh&quantity=N.
---
Validação em formulários: onde colocar a lógica
Client-side (UX rápida) vs server-side (fonte da verdade)
Validação no cliente devolve feedback imediato sem latência de rede. Mas JavaScript pode ser contornado. A fonte da verdade é o servidor.
Padrão recomendado: validar sintaxe no cliente, checksum no servidor
No cliente: comprimento, máscara, caracteres permitidos. No servidor: checksum completo mais rejeição de sequências inválidas. Essa separação evita round-trips desnecessários sem abrir a verificação de integridade a bypass.
Exemplo de endpoint Next.js reutilizável para múltiplos tipos de documento
// src/app/api/validate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { validarCPF } from '@/lib/validators/cpf';
import { validarCNPJ } from '@/lib/validators/cnpj';
import { validarCNH } from '@/lib/validators/cnh';
import { validarCIN } from '@/lib/validators/cin';
const validators: Record<string, (v: string) => boolean> = {
cpf: validarCPF,
cnpj: validarCNPJ,
cnh: validarCNH,
cin: validarCIN,
};
export async function POST(req: NextRequest) {
const { type, value } = await req.json();
const validate = validators[type];
if (!validate) {
return NextResponse.json(
{ valid: false, reason: 'tipo desconhecido' },
{ status: 400 }
);
}
const valid = validate(value ?? '');
return NextResponse.json({ valid, reason: valid ? null : 'checksum inválido' });
}LGPD Art. 6º, I e Art. 9º: minimização de dados coletados em formulários
A LGPD Art. 6º, I exige finalidade legítima e específica para o tratamento de dados pessoais. O Art. 9º obriga informar ao titular quais dados são coletados e por quê. Na prática: não colete CPF se a funcionalidade não exige verificação de identidade. Campos opcionais devem ser opcionais de verdade.
---
Geração de dados fictícios para testes (LGPD Art. 12)
Por que usar dados sintéticos em vez de CPFs/CNPJs reais em seeds
Usar CPFs e CNPJs reais em ambientes de desenvolvimento viola o princípio de minimização e expõe dados de terceiros a quem não deveria tê-los. Um dump de produção com CPFs reais em staging é um incidente de segurança, não um atalho de desenvolvimento.
O que a LGPD diz sobre dados anonimizados e pseudonimizados (Art. 12 e Art. 13)
O Art. 12 da LGPD define que dados que não podem ser revertidos para identificar o titular não são dados pessoais. Dados sintéticos gerados por algoritmo, sem base em registros reais, se enquadram nessa categoria. O Art. 13 permite uso de dados pseudonimizados para pesquisa, mas com separação do mapeamento de reversão.
DICA: Documentos gerados pelo FakeForge BR são produzidos por algoritmo sem consulta a nenhuma base da Receita Federal ou SSP. O checksum é válido, mas o número nunca foi emitido. Isso os torna seguros para seeds, testes de carga e fixtures de CI.
Como FakeForge BR gera documentos válidos no formato sem usar registros reais
Cada gerador sorteia dígitos aleatórios para a parte base, calcula os DVs pelo mesmo algoritmo dos órgãos emissores e rejeita sequências inválidas (todos iguais, CPFs de região inexistente). O resultado passa em qualquer validador de formato.
Integração via API REST: /api/generate?type=cpf&quantity=100&format=json
curl "https://fakeforge.com.br/api/generate?type=cpf&quantity=100&format=json"Retorna um array de 100 CPFs prontos para seeds. Substitua cpf por cnpj, cnh, cin ou pessoa conforme a necessidade. A documentação completa está em /docs.
---
Erros comuns de implementação e como evitá-los
Armazenar CPF/CNPJ com máscara no banco (coluna VARCHAR vs CHAR sem formatação)
Armazenar "123.456.789-09" em vez de "12345678909" desperdiça 4 bytes por registro, impede índice eficiente e força tratamento inconsistente em queries de JOIN. Use CHAR(11) para CPF e CHAR(14) para CNPJ, sem máscara.
CREATE TABLE pessoas (
id SERIAL PRIMARY KEY,
cpf CHAR(11) UNIQUE NOT NULL,
cnpj CHAR(14) NULL,
rg VARCHAR(20) NULL,
cin CHAR(12) NULL,
cnh CHAR(11) NULL,
nome VARCHAR(200) NOT NULL,
criado_em TIMESTAMPTZ DEFAULT NOW()
);Aceitar "000.000.000-00" e CNPJs zerados (sequências inválidas mesmo com DV correto)
Sequências homogêneas geram DVs válidos matematicamente mas nunca são emitidas. O guard if (/^(.)\1{13}$/.test(s)) return false na entrada da função resolve o caso.
Regex que rejeita CNPJ alfanumérico antes de validar checksum
/^\d{14}$/ retorna false para CNPJs como B3QK4L70001XX a partir de julho de 2026. Atualize para ^[A-Z0-9]{14}$ após normalização para maiúsculas e remoção de máscara.
Não normalizar entrada antes de calcular (espaços, pontos, barras, hífens)
Toda função de validação deve começar com normalizarDocumento(). Centralizar elimina falsos negativos causados por diferenças de formatação e garante que a mesma lógica funcione para input de formulário, leitura de arquivo CSV e payload de API.
---
Resumo
| Documento | Algoritmo | Casos especiais | Ferramenta |
|---|---|---|---|
| CPF | Mod 11, pesos 10..2 e 11..2 | Rejeitar 11 sequências homogêneas | /gerador-cpf |
| CNPJ (num.) | Mod 11, pesos cíclicos | Rejeitar sequências iguais | /gerador-cnpj |
| CNPJ (alfa) | Idem, charCode para letras | Atualizar regex e máscara de input | /gerador-cnpj-alfanumerico |
| RG | Por UF (SP: mod 11 + X) | Sem validação cross-state | /gerador-rg |
| CIN | Mod 11, pesos 2..10 | Coexiste com RG até 2032 | /gerador-cin |
| CNH | Dois DVs com pesos e DSC distintos | Sequências homogêneas são inválidas | /gerador-cnh |
Próximos passos:
- Adicione
normalizarDocumento()como pré-processador único em todas as funções de validação existentes no projeto. - Atualize regex de CNPJ para
[A-Z0-9]{14}antes de julho de 2026. - Migre colunas de CPF/CNPJ para
CHAR(11)eCHAR(14)sem máscara, com índice único. - Crie colunas
cinergseparadas para suportar o período de coexistência até 2032. - Substitua CPFs e CNPJs reais em seeds por dados gerados via API REST do FakeForge.
- Use /gerador-pessoa para fixtures completas com todos os documentos correlacionados em um único request.
Perguntas frequentes
Como fazer migração de colunas de CPF/CNPJ com máscara (VARCHAR) para CHAR sem máscara em produção sem downtime?+
Use trigger que normaliza entrada nova antes do INSERT. Backfill em chunks noturnos com `UPDATE tabela SET cpf = regexp_replace(cpf, '\D', '', 'g') WHERE id BETWEEN x AND y`. Crie índice único novo em paralelo. Após 100% backfill, remova trigger. PostgreSQL suporta isso sem lock de tabela inteira. Dois comandos: ALTER TABLE ADD CONSTRAINT; DROP old_column.
Como testar validação de CPF em CI/CD sem expor dados reais em logs?+
Chame API do FakeForge antes dos testes para gerar CPFs sintéticos determinísticos (use seed fixo). Crie fixture que popula banco de testes em memória. Logs contêm apenas números gerados, nunca reais. Mock de rede não funciona; integração real é mais segura. Limpe dados pós-teste com teardown().
Se um CPF passa na validação de checksum, posso usá-lo para fins de contato ou assumir que é real?+
Não. Checksum valida apenas formato, não emissão. Dados sintéticos passam no checksum mas nunca foram emitidos. Para LGPD/contato, exija confirmação secundária: email, SMS, OTP. Mantenha coluna `origem` (real | sintético) para auditoria. Recuse contatos baseados em CPF sintético.
Como fazer rollout de CNPJ alfanumérico (julho/2026) sem quebrar validação legada?+
Crie função única `validarCNPJUnificado()` que aceita `[A-Z0-9]{14}` após normalizar para maiúsculas. Novo regex é backward-compatible (0-9 já passam). Mantenha coluna CHAR(14) existente. Teste novo validador com dados mixtos antes de deploy. Sem downtime; apenas atualizar função e redeploy.
Qual é o risco de aceitar e armazenar um CPF real que vem de uma API terceira ou webhook?+
Vazamento de dados reais de terceiros sem consentimento. LGPD Art. 6º exige finalidade legítima. Alternativa: não armazene, apenas valide checksum, marque como "verificado externamente", guarde hash + últimos 4 dígitos para referência. Se precisar armazenar, obtenha consentimento explícito. Compliance vale o espaço economizado.