MD5 vs SHA-1 vs SHA-256: qual hash usar?
Use SHA-256 em tudo o que envolve segurança; MD5 e SHA-1 só são aceitáveis quando ninguém ganha nada forjando um hash igual.
MD5, SHA-1 e SHA-256 fazem o mesmo trabalho: transformam uma entrada de qualquer tamanho em uma impressão digital curta e de tamanho fixo, chamada de resumo (em inglês, digest). A mesma entrada sempre gera o mesmo resumo, e mudar um único bit muda o resultado inteiro. Os três diferem no tamanho do resumo, na velocidade e, principalmente, em uma questão: alguém consegue produzir de propósito duas entradas diferentes com o mesmo resumo? Com MD5 e SHA-1 isso já foi feito publicamente. Com SHA-256, não.
Os três algoritmos lado a lado
| MD5 | SHA-1 | SHA-256 | |
|---|---|---|---|
| Tamanho do resumo | 128 bits, 32 caracteres hexadecimais | 160 bits, 40 caracteres hexadecimais | 256 bits, 64 caracteres hexadecimais |
| Publicação | 1992 (RFC 1321) | 1995 (FIPS 180-1) | 2001, como parte da família SHA-2 |
| Colisões | Práticas desde 2004 | Práticas desde 2017 | Nenhum ataque conhecido |
| Encontrar uma entrada para um resumo dado | Nenhum ataque prático | Nenhum ataque prático | Nenhum ataque prático |
| Serve hoje para | Checksums sem fins de segurança, chaves de cache | Só compatibilidade com sistemas legados | O padrão para qualquer projeto novo |
Estes são os resumos completos dos cinco bytes hello, como impressos por md5sum, sha1sum e sha256sum. O Gerador de Hash mostra os mesmos valores lado a lado, além de SHA-384 e SHA-512, para um texto ou para um arquivo de até 50 MB.
MD5 5d41402abc4b2a76b9719d911017c592
SHA-1 aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
SHA-256 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
O que os bits a mais compram
O tamanho do resumo define o custo de um ataque genérico, aquele que trata a função como uma caixa-preta. Por causa do paradoxo do aniversário, encontrar quaisquer duas entradas que colidem exige cerca de 2^(n/2) tentativas para um resumo de n bits: 2^64 para o MD5, 2^80 para o SHA-1 e 2^128 para o SHA-256. Encontrar uma entrada que corresponda a um resumo específico (uma pré-imagem) exige cerca de 2^n.
Esses números só valem enquanto o projeto do algoritmo não tem atalhos. MD5 e SHA-1 têm atalhos para gerar colisões, e é por isso que o tamanho deles já não conta a história toda. O SHA-256 não tem nenhum atalho conhecido, e 2^128 está muito além de todo o poder computacional que existe.
Como o MD5 e o SHA-1 foram quebrados
Um ataque de colisão não permite que ninguém reverta um hash. Ele permite que o atacante prepare de antemão duas entradas diferentes com o mesmo resumo, faça uma delas ser aceita como confiável e depois a troque pela outra. A variante perigosa é a colisão de prefixo escolhido (chosen-prefix): o atacante escolhe dois inícios com significado, como dois certificados com nomes diferentes, e calcula bytes extras que tornam os resumos iguais.
- MD5, 2004: Xiaoyun Wang e colegas publicaram colisões práticas de MD5.
- MD5, 2008: uma equipe de pesquisadores usou uma colisão MD5 de prefixo escolhido para criar um certificado falso de autoridade certificadora em que os navegadores teriam confiado, porque uma CA comercial ainda assinava certificados com MD5.
- MD5, 2012: o malware Flame usou uma colisão MD5 para forjar um certificado de assinatura de código da Microsoft.
- SHA-1, 2017: o Google e o CWI Amsterdam anunciaram o SHAttered, dois arquivos PDF diferentes com o mesmo resumo SHA-1. A equipe relatou cerca de 6.500 anos de CPU e 110 anos de GPU de processamento.
- SHA-1, 2020: Gaëtan Leurent e Thomas Peyrin publicaram uma colisão SHA-1 prática de prefixo escolhido e a usaram para se passar por uma chave PGP.
Os navegadores deixaram de aceitar certificados TLS com SHA-1 em 2017, e o NIST anunciou em dezembro de 2022 que o SHA-1 deve ser abandonado por completo até o fim de 2030. Nem o MD5 nem o SHA-1 têm um ataque prático de pré-imagem: tendo apenas o resumo, ninguém consegue encontrar uma entrada que o produza. É por isso que os dois ainda funcionam para detectar danos acidentais.
Velocidade: medida, não suposta
O MD5 costuma ser descrito como o mais rápido. Calculamos o hash dos mesmos 256 MB de dados aleatórios com o Node 24 (OpenSSL 3.5) em um computador de mesa, três execuções para cada algoritmo:
| Algoritmo | Vazão nessa máquina |
|---|---|
| SHA-1 | cerca de 1.000 MB/s |
| MD5 | cerca de 730 MB/s |
| SHA-512 | cerca de 690 MB/s |
| SHA-256 | cerca de 460 MB/s |
A ordem depende do processador. Essa CPU não tem as extensões do conjunto de instruções SHA; processadores que as têm aceleram o SHA-1 e o SHA-256 em hardware, e aí o SHA-256 pode passar o MD5. O SHA-512 venceu o SHA-256 aqui porque trabalha com palavras de 64 bits. Com entradas curtas a diferença some: um milhão de hashes de um endereço de e-mail curto levou entre 1,8 e 2,1 segundos com cada um dos três, MD5, SHA-1 e SHA-256, porque o custo de cada chamada pesa mais do que o próprio cálculo do hash. Velocidade quase nunca é um motivo real para escolher o MD5.
Onde cada um ainda serve
- MD5: detectar corrupção acidental, eliminar duplicatas entre os seus próprios arquivos, chaves de cache e ETags, e conversar com sistemas legados que esperam esse formato. Nunca para assinaturas, certificados ou verificações de integridade em que um atacante possa fornecer o arquivo.
- SHA-1: sistemas existentes que você ainda não pode mudar. O Git nomeia objetos com SHA-1 há muito tempo, usando uma implementação reforçada que detecta os ataques de colisão conhecidos, e também aceita repositórios que usam SHA-256. O HMAC-SHA1, algoritmo padrão por trás dos códigos TOTP dos apps autenticadores, não é afetado por ataques de colisão.
- SHA-256: checksums de downloads, assinaturas digitais, certificados, HMAC, endereçamento por conteúdo e qualquer coisa nova. Use o SHA-256 quando um checksum precisa resistir a adulteração, e deixe o MD5 para os valores legados que você ainda precisa conferir.
Vai conferir um download? O Verificador de Checksum de Arquivos calcula o hash do arquivo localmente e informa com qual algoritmo um checksum colado corresponde.
Nenhum deles serve para guardar senhas
Os três foram projetados para ser rápidos, e rapidez é exatamente o que quer um atacante que testa bilhões de senhas. Um salt impede o uso de tabelas pré-calculadas, mas não deixa as tentativas mais lentas. Armazenar senhas exige uma função deliberadamente lenta e, de preferência, que consuma muita memória (memory-hard). Na mesma máquina do teste acima:
| Função e parâmetros | Tempo por senha |
|---|---|
| SHA-256, uma chamada no Node | cerca de 2,5 microssegundos |
| Argon2id, 19 MiB de memória, 2 iterações, 1 via (lane) | cerca de 33 milissegundos |
| PBKDF2-HMAC-SHA256, 600.000 iterações | cerca de 270 milissegundos |
| scrypt, N=2^17, r=8, p=1 | cerca de 310 milissegundos |
Esses parâmetros são os mínimos do OWASP Password Storage Cheat Sheet, que recomenda primeiro o Argon2id, depois o scrypt, o bcrypt com fator de trabalho 10 ou maior para sistemas legados e o PBKDF2 quando a conformidade com FIPS-140 é obrigatória. Use a API de hash de senhas do seu framework em vez de montar uma solução a partir de um hash comum.
HMAC: quando o hash precisa de uma chave
Para provar que uma mensagem veio de alguém que conhece um segredo, não calcule sha256(secret + message). MD5, SHA-1 e SHA-256 são todos vulneráveis a extensão de comprimento (length extension): conhecer o resumo de uma mensagem permite ao atacante calcular um resumo válido para essa mensagem com dados extras no final, sem saber o segredo. O HMAC (RFC 2104) evita isso. O HMAC-SHA256 é a escolha padrão, e todos os trechos abaixo imprimem o mesmo valor:
// Node.js
crypto.createHmac('sha256', 'secret').update('hello').digest('hex')
# Python
hmac.new(b'secret', b'hello', hashlib.sha256).hexdigest()
// PHP
hash_hmac('sha256', 'hello', 'secret')
// 88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b
Compare HMACs com uma função de tempo constante, como crypto.timingSafeEqual, hmac.compare_digest ou hash_equals, e não com ==.
Calcular hashes no terminal e no código
# Linux (coreutils)
md5sum file.iso
sha1sum file.iso
sha256sum file.iso
# macOS
md5 file.iso
shasum -a 1 file.iso
shasum -a 256 file.iso
# Windows PowerShell
Get-FileHash file.iso -Algorithm MD5
Get-FileHash file.iso -Algorithm SHA1
Get-FileHash file.iso # SHA256 is the default
# Any system with OpenSSL
openssl dgst -sha256 file.iso
# Hashing a string: printf adds no newline, echo does
printf '%s' hello | sha256sum # 2cf24dba5fb0a30e...
echo hello | sha256sum # 5891b5b522d5df08...
Repare nas duas últimas linhas: o printf não acrescenta quebra de linha e o echo acrescenta, por isso os resumos são diferentes. No PowerShell, Get-FileHash usa SHA256 quando nenhum algoritmo é informado. No navegador, crypto.subtle.digest() aceita SHA-1, SHA-256, SHA-384 e SHA-512, mas não MD5; pedir MD5 lança um NotSupportedError. É por isso que as ferramentas de navegador, incluindo as deste site, calculam o MD5 com uma implementação separada em JavaScript.