Toda vez que alguém propõe usar hash como identificador, aparece a pergunta: e se der colisão?
A resposta padrão é citar o número de átomos do universo observável e seguir em frente. O número está certo e a resposta é preguiçosa, porque ela responde a pergunta errada.
Existem dois tipos de colisão, com riscos completamente diferentes, e a conta de probabilidade só cobre um deles.
Colisão acidental
É a que a matemática descreve. Dois valores diferentes produzindo o mesmo hash por azar.
O paradoxo do aniversário diz que, com uma saída de N bits, a probabilidade fica relevante por volta de 2^(N/2) itens.
Para SHA-256, isso é 2^128. Um número que não tem uso prático: se você gerasse um bilhão de hashes por segundo desde o Big Bang, ainda não chegaria perto.
Para uma saída truncada em 64 bits, muda tudo: a chance vira apreciável por volta de 4 bilhões de itens, o que é um volume que sistemas reais alcançam.
E aqui está a primeira armadilha de verdade. Ninguém usa SHA-256 completo como identificador em URL, porque 64 caracteres hexadecimais é feio. Trunca-se pra 12, 16, 20 caracteres. Cada truncamento corta o expoente pela metade.
64 hex (256 bits) -> colisão em ~2^128 itens. irrelevante
16 hex ( 64 bits) -> colisão em ~2^32 itens. 4 bilhões
12 hex ( 48 bits) -> colisão em ~2^24 itens. 16 milhões
8 hex ( 32 bits) -> colisão em ~2^16 itens. 65 mil
Oito caracteres, que é o que o Git mostra por padrão, colide de verdade em repositório grande. O Linux kernel já precisou aumentar o padrão por causa disso.
Então a pergunta certa não é “SHA-256 colide?”. É “quantos bits eu realmente guardei?”.
Colisão intencional
Essa é a que importa e a que a probabilidade não descreve.
A pergunta não é se o azar acontece. É se alguém consegue fabricar dois documentos diferentes com o mesmo hash de propósito.
Para MD5, isso é trivial: gera-se um par colidente em segundos num notebook. Para SHA-1, é caro e já foi demonstrado publicamente, o que na prática significa quebrado.
O ataque relevante nem sempre é a colisão pura. É a colisão com prefixo escolhido, que permite construir dois arquivos com conteúdo controlado e mesmo hash. Com isso se assina o documento benigno e se apresenta o malicioso.
Se existe alguém interessado em enganar o seu sistema, a força do algoritmo importa e não tem substituto. SHA-256 ou BLAKE3, sem truncamento agressivo.
O risco que ninguém calcula
Na prática, a maior parte das “colisões” que eu vejo em produção não é colisão criptográfica coisa nenhuma. É serialização instável.
Dois registros diferentes que produzem o mesmo hash porque a forma canônica esqueceu um campo. Ou o mesmo registro produzindo hashes diferentes porque a ordem das chaves do JSON mudou.
// campos concatenados com hífen
["ab", "c"] -> "ab-c"
["a", "bc"] -> "a-bc"
// parecem seguros. agora com um valor contendo hífen:
["a-b", "c"] -> "a-b-c"
["a", "b-c"] -> "a-b-c" // colisão, e não foi o SHA-256 que falhou
Isso não é probabilidade astronômica. É bug de engenharia, acontece com cem registros, e é a causa real de quase todo problema que se apresenta como colisão. Escrevi sobre a forma canônica em system design de rastreabilidade com hash único.
A decisão prática
Três perguntas resolvem o caso.
Existe adversário? Se sim, algoritmo criptográfico forte, sem truncar demais. Se não, um hash rápido não criptográfico serve e é mais barato.
Quantos bits sobram depois do truncamento? Faça a conta com o volume que você espera em cinco anos, com margem. Como regra, 128 bits é confortável pra qualquer coisa, 64 bits exige pensar, 32 bits só serve pra deduplicação com verificação depois.
O que acontece se colidir? Essa é a pergunta que quase ninguém faz, e é a que decide quanto esforço vale.
Se a colisão significa “dois arquivos idênticos foram deduplicados por engano” e existe verificação de conteúdo depois, o custo é baixo.
Se significa “duas transações financeiras diferentes viraram uma”, o custo é alto e você quer margem enorme.
A defesa mais barata
Uma constraint única no banco.
alter table trace_event add constraint trace_event_hash_unique unique (tenant_id, hash);
Se acontecer o improvável, o insert falha, alguém é alertado, e nada corrompe silenciosamente. Custa nada e transforma a categoria “corrupção invisível” em “erro visível”.
É a mesma filosofia da verificação de balanceamento num ledger: não previne o impossível, garante que o impossível não passe despercebido.
O resumo
Colisão acidental em SHA-256 completo não é um risco que mereça o seu tempo.
Colisão em hash truncado é um risco de engenharia, com conta simples.
Colisão intencional é risco real e depende só de escolher um algoritmo que não esteja quebrado.
E a colisão por serialização instável, que é a única que eu vi acontecer de verdade, não aparece em nenhuma dessas contas. Ela vem de não ter escrito a forma canônica antes de escrever o hash.