Ask HN: Enfrentei uma colisão real de UUID v4...
(news.ycombinator.com)- O banco de dados detectou hoje um UUID v4 duplicado, e o valor existente era exatamente igual a
b6133fd6-70fe-4fe3-bed6-8ca8fc9386cd, de um registro adicionado em 2025 - O pacote em uso é o uuid do npm, e dizem que o processo é gerar com
import { v4 as uuidv4 } from "uuid";e depoisconst document_id = uuidv4();, inserindo então no banco de dados - Há apenas cerca de 15.000 registros no banco de dados, então isso parece estatisticamente impossível, e a pessoa está perguntando se mais alguém já passou pelo mesmo
1 comentários
Comentários do Hacker News
jandrewrogers: Isso é surpreendentemente comum. A segurança do UUIDv4 depende da premissa de que existe uma fonte de entropia de alta qualidade, mas essa premissa quebra facilmente por falhas de hardware, bugs comuns de software e pela falta de compreensão dos desenvolvedores sobre entropia
Detectar que a fonte de entropia está quebrada é bem caro, então quase ninguém faz isso, e no fim só descobre depois que a colisão acontece. Por isso, em muitos sistemas de alta confiabilidade e alta garantia, o UUIDv4 é explicitamente proibido
Quanto mais fontes de entropia, melhor, e uma boa parte delas deve ser não determinística. Mesmo em jogos pequenos, se você misturar no seed inicial valores como coordenadas do mouse, intervalo entre cliques de botão e número de frames antes de apertar start, fica muito mais difícil prever, mesmo usando internamente um gerador pseudoaleatório. Eu ficaria decepcionado se a CloudFlare usasse menos de 100 fontes de entropia
Isso acontece quando você não valida o valor de retorno, como no velho caso do Go: “pedi N bytes, mas só recebi 3, então preciso pedir os N-3 bytes restantes”. Na maioria dos hardwares e sistemas operacionais isso quase nunca explode, então ninguém confere, e um dia aparece em produção como dezenas de milhares de colisões
throwaway_19sz: Parece uma história engraçada e inacreditável, mas é real. Há 10 anos um amigo entrou como CTO numa startup de alto crescimento, numa empresa com uns 200 desenvolvedores, e na primeira semana descobriu que existia um microsserviço dedicado só a gerar UUID
Havia 3 engenheiros dedicados a um único endpoint, além até de gente de banco de dados. Sempre que um time precisava de um UUID novo “seguro”, tinha que chamar esse serviço, que gerava o UUID, verificava no próprio banco se já existia um igual emitido antes, e se não existisse inseria e então retornava. Não sei se era pela paz de espírito, mas esse time ainda rodava seu próprio quadro Kanban e seus próprios sprints
Depois entrei em startups onde toda vez que alguém inventava uma nova preocupação surgiam um novo microsserviço e um novo time. A meta trimestral explicitamente era aumentar o tamanho da equipe de engenharia, e times de 3 ou 4 pessoas inventavam trabalho para si mesmos nos próprios sprints e reuniões de planejamento. Sugeri mover gente de projetos estáveis para tarefas urgentes, mas barraram isso porque entrava em conflito com o KPI de aumentar o número de engenheiros até um certo valor
Para alta disponibilidade e implantação global, também dá para shardear, dando a cada instância uma faixa dedicada de IDs. Basta reservar alguns bits mais altos para o ID do datacenter e alguns bits para a instância geradora dentro dele. Espera, acho que já vi isso em algum lugar... Fico curioso se o Twitter ainda usa esse modelo ou se acabou mudando
Todo dia recebiam um dump do banco para checar na geração de IDs “temporários”, e eles só viravam “definitivos” depois de enviados corretamente ao CMDB. Havia guard rails para impedir que IDs temporários fossem usados em produção, e até um processo para reciclar IDs definitivos não utilizados. Da última vez que soube, eles estavam havia 18 meses num projeto de 6 meses para mover o cache local de banco para o Zookeeper
CodesInChaos: Normalmente isso acontece por causa de um gerador pseudoaleatório mal semeado. Importa saber se o UUID foi criado no backend ou no frontend
O frontend é inerentemente difícil de confiar por vários motivos, inclusive colisões intencionais, então é preciso tratar colisões. No backend dá para fazer isso de forma confiável. Antigamente isso acontecia em VMs, mas hoje em dia já deveria estar resolvido; ainda assim, pode ocorrer se processos fortemente sandboxados usarem um caminho alternativo de aleatoriedade inseguro. Fork de processo ou de VM também pode criar colisões por cópia de estado
kst: Isso me lembrou uma passagem de “Pro Git”. <https://git-scm.com/book/en/v2>
O exemplo dizia que, mesmo se os 6,5 bilhões de pessoas da Terra criassem a cada segundo código no volume do histórico inteiro do kernel Linux e empurrassem tudo para um único repositório Git gigantesco, ainda levaria cerca de 2 anos para a chance de colisão de objetos SHA-1 chegar a 50%. Por isso gostei da frase de que uma colisão natural de SHA-1 é menos provável do que todos os membros da equipe morrerem na mesma noite em ataques de lobos não relacionados entre si. Hash SHA-1 não é número aleatório e tem 160 bits, então é diferente de UUIDv4, mas gostei da metáfora dos ataques de lobos não relacionados
É aquela metáfora de dar uma volta na Terra no equador a cada bilhão de anos, tirando uma gota d'água do Pacífico a cada volta e, quando o oceano secar, colocar uma folha de papel; repetir isso até a pilha chegar ao Sol, e ainda assim os três primeiros dígitos do cronômetro de 52! segundos não mudarem
e12e: Tem uma discussão relacionada aqui: https://github.com/uuidjs/uuid/issues/546
Por exemplo, há um relato de que
crypto.getRandomValues()testado no googlebot era determinísticoadyavanapalli: O que está sendo descrito aqui é tão raro que, neste exato momento, a chance de a Terra inteira ser destruída por um asteroide é maior
Lembro de ter ouvido sobre uma mulher que realmente foi atingida por um meteorito e sobreviveu com ferimento na perna. Se houve colisão de UUID, a chance esmagadoramente maior é de bug de software ou falha no computador, embora também possa ser raio cósmico. Raios cósmicos mexendo em memória ou CPU são mais comuns do que as pessoas imaginam
juancn: Não seria inicialização estranha do gerador aleatório ou falta de entropia? Se ninguém customizou nada, ele usa
crypto.getRandomValues(rnds8), egetRandomValuesnão especifica uma quantidade mínima de entropiaGeee: Pela interpretação de muitos mundos da mecânica quântica, deve existir algum ramo do universo em que todos os UUIDs são iguais. Fico imaginando o que as pessoas de lá pensam
mittermayr: Concordo totalmente que isso não faz sentido. Ainda assim, chutando, antes talvez o UUIDv4 fosse gerado no celular do usuário e enviado ao banco, enquanto hoje de manhã o UUID que colidiu foi gerado num servidor Ubuntu
Não sei exatamente como o UUIDv4 é gerado nem se características da máquina entram no algoritmo, mas a única mudança que me vem à cabeça é que antes era gerado no dispositivo e, há alguns meses, passou a ser gerado no servidor
Mas no servidor, especialmente em 2026, isso não deveria acontecer. Antigamente seed de aleatoriedade em VM já foi um problema, mas hoje deveria ser menos. Mesmo que um dos UUIDs tenha sido gerado de forma ruim, a chance de um UUID realmente aleatório colidir com ele ainda é muito baixa; então os dois geradores provavelmente precisariam estar com problema
dweez: Hora de reler este texto divertido: https://jasonfantl.com/posts/Universal-Unique-IDs/
Se transformássemos o universo inteiro em um computador gigante dedicado a gerar UUIDs até a morte térmica, de quantos bits o espaço de IDs precisaria?
beejiu: Fiquei curioso se o UUID é gerado no cliente ou no servidor. Se for no cliente, pode ser por causa de bots de crawler. Por exemplo, o Googlebot executa JavaScript com “aleatoriedade” determinística
Explicações desse tipo são várias ordens de grandeza mais plausíveis do que uma colisão realmente aleatória
merlindru: Tem cara de problema de seed. Se você conseguir provar que não é, talvez fique um pouco famoso
erlkonig: Sempre digo ao time que, com dados suficientes, valores aleatórios eventualmente podem colidir, e aí descobrimos quão robusto o software realmente é
Mesmo assim, há muitos desenvolvedores experientes, líderes de equipe e CIOs que acreditam que isso é impossível e não escrevem nenhum código para tratar a situação. Aí um gerador aleatório ruim pode destruir o sistema muito antes do esperado, com corrupção simultânea inclusive sem detecção e regeneração. Me parece da mesma categoria de quem não verifica se
malloc()deu certo. Costumo perguntar: “se é impossível, então não estamos usando bits demais?”leni536: Não foi acaso; tem um bug em algum lugar. Pelo que vi por alto, o pacote parece chamar
crypto.randomUUID()do runtime JS, e isso sempre deveria estar corretamente semeadoA possibilidade de haver bug no runtime parece extremamente baixa, mas nunca se sabe. Fiquei curioso sobre qual runtime JS estão usando
jbverschoor: A causa mais plausível é que o pacote de geração aleatória de que
uuiddepende foi comprometido recentemente para tornar os números “aleatórios” previsíveis. Como resultado, um ataque de supply chain pode ter colocado em risco vários projetos de criptografia, SSL e moedasuuid/src/rng.ts, o array aleatório virouconst. Todas as chamadas passaram a compartilhar o mesmo array aleatórioComo chamadas subsequentes atualizam o código aleatório anterior, boa sorte se você gerou algo importante. O código antigo fazia um
slice()para criar uma cópia nova. Pode ter sido uma mudança não intencional, mas eu nem entendo como isso passou, porque um teste que gerasse dois números aleatórios e verificasse se são diferentes provavelmente já falhariapif: Mesmo com uma fonte de entropia de alta qualidade, você não consegue transformar “provavelmente será assim” em “necessariamente será assim”. Se você precisa de algo difícil de adivinhar, procure a criptografia; mas se precisa de unicidade garantida, precisa construir isso você mesmo
athrowaway3z: Uma regra prática simples é pensar se dá para colocar um timestamp no ID além do valor aleatório. Na maioria das vezes, a resposta é sim, e UUIDv7 já basta
Se você analisou o problema a ponto de conseguir escrever uma prova de que o vazamento de informação é inaceitável, parabéns. Seu sistema provavelmente já é complexo e lento o suficiente para usar um hash criptográfico forte ou, se quiser evitar trabalho, UUIDv5
darqis: O PostgreSQL 18 tem suporte nativo a uuidv7, e o padrão pode ser
uniquecomuuid7()tumdum_: É um gerador pseudoaleatório com seed ruim
serf: É algo na ordem de 1 em 4,72 × 10²⁸, ou seja, 1 em 47,3 octilhões. Se fosse real, antes de comprar bilhete de loteria eu suspeitaria de condição de corrida ou de algum outro erro simples
evnix: Mesmo deixando a matemática de probabilidades de lado, a realidade em que vivemos é tal que, mesmo com o melhor gerador aleatório de hardware, a aleatoriedade pode ser menor do que parece
Onde segurança não é tão crítica, eu migraria para algo como TSID ou então para uuidv7, para que isso praticamente não aconteça no mundo real. Acho melhor do que complicar demais o código com retries
jordiburgos: Só peço que não usem
b6133fd6-70fe-4fe3-bed6-8ca8fc9386cd. Fui checar no meu banco e já estava em uso16b55183-1697-496e-bc8a-854eb9aae0f3e provavelmente há mais alguns. Se todo mundo postar aqui sua lista, não daria para verificar duplicatas?pyuser583: Fiquei curioso sobre qual UUID é preferido hoje em dia
smokel: Já aconteceu várias vezes de eu culpar compilador, raio cósmico, efeito quântico ou no mínimo algum bug obscuro de kernel, para depois descobrir que o culpado era eu mesmo
Colisão em 15.000 registros é improvável demais, então eu suspeitaria primeiro de outras causas: tratamento de duplicatas, requisições reenviadas, objeto reutilizado, logs enganosos, reutilização de identificadores em outro caminho de código. Se você compartilhar um pouco mais do código ao redor, o pessoal talvez consiga ajudar a revisar
wazoox: Ainda não aconteceu comigo, mas há dois dias encontrei isso nas profundezas de um código PHP em produção: uma função
createUUID()que montava algo com cara de UUID cortando e colando o valor demd5(uniqid('', true))Não faço ideia de como esse horror ainda não mordeu nossa jugular
sedatk: O
uuidjs/uuidtem um aviso de que clientes com geradores aleatórios determinísticos, como o Googlebot, podem gerar UUIDs duplicadosIsso pode ser um problema para apps que esperam que UUIDs gerados no cliente sejam sempre únicos, então a estratégia recomendada é verificar duplicatas e falhar de forma elegante, ou desabilitar operações de escrita para clientes Googlebot: https://github.com/uuidjs/uuid/commit/91805f665c38b691ac2cbd...
xyzzy123: Já tive um teste de carga prolongado falhando por UUID duplicado em um sistema distribuído baseado em Linux
Depois de muita investigação, descobrimos que era um bug de kernel, mais especificamente uma condição de corrida. Em sistema multiprocessado, se dois processos lessem
/dev/randomexatamente ao mesmo tempo, em casos muito raros, algo como uma em um milhão, podiam receber os mesmos bytes. Eu começaria olhando a inicialização do gerador aleatóriobaq: Parece que a VM em execução virtualizou toda a entropia até ela sumir
glaslong: Preciso comprar umas lâmpadas de lava
0xfffafaCrash: Fiquei curioso se o UUID foi gerado no frontend ou no backend. Se foi no frontend, em vez de problema de entropia eu apostaria na possibilidade de o código cliente ou a requisição ter sido manipulada para injetar um UUID já conhecido
latentframe: Uma das expressões mais perigosas em engenharia é estatisticamente impossível. Em escala suficiente, casos extremos deixam de ser teoria e viram evento operacional
8organicbits: No ano passado escrevi sobre uma colisão real, incluindo a biblioteca envolvida: https://alexsci.com/blog/uuid-oops/
Para UUID ter resistência a colisão, há muitas restrições que precisam ser seguidas com rigor, e neste caso parece bem provável que o problema esteja no gerador aleatório
nu11ptr: No fim das contas é problema de fonte de entropia. Por isso eu sempre gero e insiro dentro de um loop. Se houver colisão, dá para tratar de forma elegante
sbuttgereit: Não é “tecnicamente impossível”. É tecnicamente possível, sim. Com boa aleatoriedade, apenas extremamente, extremamente improvável; mas não existe nada em UUIDv4 que impeça tecnicamente a geração de valores duplicados
beardyw: Pode ser uma pergunta boba, mas não daria para anexar a data, nem que fosse em segundos em hexadecimal? Parece que adicionar só alguns bytes já garantiria que o que funciona hoje continue funcionando no futuro
mdavid626: Também pode haver outras explicações. Por exemplo, alguém pode ter mexido manualmente na requisição ou no banco
radial_symmetry: Já passei por algo assim uma vez e achei que eu estava enlouquecendo; ler os comentários aqui me tranquilizou
NKosmatos: Não é “tecnicamente impossível”. Não é impossível; é só extremamente, extremamente improvável. Acho que vale comprar um bilhete de loteria
Sempre que vejo a palavra “improvável”, lembro de https://hitchhikers.fandom.com/wiki/Infinite_Improbability_D...
sudb: Pela primeira vez senti a recompensa de ter escolhido CUID2 num dos meus projetos: https://github.com/paralleldrive/cuid2