Três operações, um mal-entendido
As três técnicas embaralham texto, mas resolvem problemas diferentes. Encoding quer caber num canal e ser lido de novo; hashing quer uma impressão digital que prove que nada mudou; criptografia quer esconder o conteúdo de quem não tem a chave. Em vez de decorar exemplos, guarde os dois eixos que separam tudo: a reversibilidade e a presença de uma chave. Encoding é reversível sem chave; criptografia é reversível com chave; hashing não é reversível de forma alguma.
Encoding
- Reversível? Sim, por qualquer um.
- Precisa de chave? Não.
- Serve para: transportar e representar bytes (Base64, hex, URL).
Criptografia
- Reversível? Sim, com a chave certa.
- Precisa de chave? Sim, esse é o ponto.
- Serve para: confidencialidade (AES, RSA).
Hashing
- Reversível? Não, por projeto.
- Precisa de chave? Não (só o HMAC usa uma).
- Serve para: integridade, impressão digital, senhas com sal.
O teste mais rápido é passar o mesmo texto pelas três operações e ver o que dá para desfazer em cada caso. Abaixo, a palavra senha123 vira três saídas bem diferentes, e só uma delas depende de um segredo para voltar ao original.
Entrada / Input: senha123
Base64 -> c2VuaGExMjM=
reverte sem segredo: qualquer um lê "senha123" de volta
reverses with no secret: anyone reads "senha123" back
SHA-256 -> 55a5e9e78207b4df8699d60886fa070079463547b095d1a05bc719bb4e6cd251
irreversível: só dá para adivinhar a entrada e recalcular
irreversible: you can only guess the input and recompute
AES-GCM -> (bundle base64, muda a cada execução)
reverte apenas com a senha correta
reverses only with the correct passphraseEncoding: representar, não proteger
Encoding traduz dados de um formato para outro por uma regra pública e reversível. O objetivo é caber em um canal. Base64, definido na RFC 4648, agrupa três bytes (24 bits) em quatro caracteres de 6 bits tirados de um alfabeto de 64 símbolos, para mandar binário por e-mail ou em JSON sem quebrar nada. O hexadecimal mostra cada byte como dois dígitos legíveis. O URL-encoding escapa caracteres especiais dentro de um link. Não há chave nem segredo em nenhum deles: quem tem o valor codificado sempre recupera o original.
"oi" -> Base64 -> "b2k=" (qualquer um reverte / anyone reverses)
"oi" -> hex -> "6f69"
"a b" -> URL-encode -> "a%20b"
// Base64url (RFC 4648 §5): variante URL-safe, troca + por - e / por _,
// e descarta o padding "=". É a codificação de tokens, JWT e chaves em URL.Hashing: a impressão digital irreversível
Uma função de hash transforma qualquer entrada em uma saída de tamanho fixo. É determinística (a mesma entrada dá sempre a mesma saída), sensível (mudar um bit muda o resultado inteiro, o efeito avalanche) e, quando criptográfica, inviável de reverter. As famílias SHA-1 e SHA-2 (SHA-256, SHA-384, SHA-512) estão na norma FIPS 180-4 do NIST; a família SHA-3, baseada no Keccak, está na FIPS 202, aprovada em 5 de agosto de 2015. Não existe descriptografar um hash: a verificação é recalcular o hash e comparar. Um checksum como o CRC32 não é hash criptográfico, detecta erro acidental, não adulteração; é da mesma família do dígito verificador do CPF e do CNPJ.
A força de um hash criptográfico se mede por três resistências. Elas não caem juntas: uma função pode ainda resistir à pré-imagem e já ter perdido a resistência à colisão, foi exatamente o que aconteceu com o SHA-1.
- Resistência à pré-imagem
- Dado um hash h, é inviável achar qualquer entrada m com hash(m) = h. É o que impede reverter o digest de um arquivo ou de uma senha.
- Resistência à segunda pré-imagem
- Dada uma entrada m1, é inviável achar uma m2 diferente com o mesmo hash. Protege um documento específico contra ser substituído por outro com o mesmo digest.
- Resistência à colisão
- É inviável achar qualquer par m1 diferente de m2 com o mesmo hash. É a mais fácil de quebrar (limite do aniversário) e foi a primeira a cair no MD5 e no SHA-1.
Por que a colisão é a mais fácil? Pelo paradoxo do aniversário. Achar uma entrada que bate com um hash fixo custa cerca de 2^n tentativas (n é o tamanho do hash em bits). Mas achar duas entradas quaisquer que colidam entre si custa só cerca de 2^(n/2), porque cada nova entrada pode colidir com todas as anteriores. É a mesma razão pela qual, numa sala de 23 pessoas, a chance de duas fazerem aniversário no mesmo dia já passa de 50%.
colisao_esperada ~= 2^(n/2) SHA-256 -> 2^128 MD5 -> 2^64- n
- tamanho da saída do hash, em bits (SHA-256 = 256, MD5 = 128)
- 2^(n/2)
- número de entradas testadas antes de esperar duas com o mesmo hash
Primeiro exemplo trabalhado, em números: dobrar o hash de 128 para 256 bits eleva o esforço ideal de colisão de 2^64 para 2^128, um fator de 2^64, quase 18,5 quintilhões de vezes mais trabalho. É por isso que 256 bits não é "o dobro" de 128: é exponencialmente mais. Mas repare na terceira coluna: no MD5, o número ideal de 2^64 é ficção. A criptanálise de 2004 derrubou o custo real para a casa de 2^24, colisões de MD5 saem em segundos num notebook. Um hash "de 128 bits" com o algoritmo quebrado não vale 2^64; vale quase nada. Tamanho de saída só protege enquanto o algoritmo por dentro continua íntegro. Gere e compare digests no gerador de hash abaixo e reconheça um digest pelo formato com o identificador de hash.
Quando um hash quebra: MD5 e SHA-1
Quebrar um hash quase nunca significa reverter a saída, significa achar uma colisão que não deveria existir com tão pouco esforço. No MD5, colisões para a função completa foram anunciadas por Wang, Feng, Lai e Yu em agosto de 2004. No SHA-1, o golpe veio em duas ondas. Em fevereiro de 2017, o ataque SHAttered da CWI e do Google produziu dois PDFs diferentes com o mesmo hash SHA-1, gastando cerca de 2^63,1 operações, bem abaixo dos 2^80 que a teoria previa. Em janeiro de 2020, Leurent e Peyrin publicaram o SHA-1 is a Shambles, uma colisão de prefixo escolhido: o atacante fixa dois começos arbitrários e diferentes e ainda assim faz os hashes baterem. É o tipo de colisão que forja certificado e assinatura, e custou cerca de US$ 45 mil em GPU alugada, barato para um atacante determinado.
Um detalhe honesto: nem toda resistência do SHA-1 caiu. A resistência à colisão foi quebrada; a resistência à pré-imagem (achar uma entrada para um hash dado) segue sem ataque prático. Mesmo assim, um hash que perdeu a resistência à colisão não serve para assinatura nem certificado, e é por isso que navegadores e autoridades certificadoras aposentaram o SHA-1. Para escolher o que usar hoje, a tabela abaixo resume o estado de cada família.
| Algoritmo | Saída | Status |
|---|---|---|
| MD5 | 128 bits | Não usar, colisões práticas desde 2004. |
| SHA-1 | 160 bits | Não usar, colisão prática (2017) e de prefixo escolhido (2020). |
| SHA-256 / SHA-512 | 256 / 512 bits | Usar, padrão atual para integridade (FIPS 180-4). |
| SHA-3 (Keccak) | 224–512 bits | Usar, construção esponja, imune a length extension (FIPS 202). |
| CRC32 / Adler-32 | 32 bits | Só erro acidental, não é criptográfico, não resiste a adulteração. |
| HMAC-SHA-256 | 256 bits | Usar, integridade e autenticidade com chave secreta. |
| bcrypt / scrypt / Argon2 | variável | Usar, só para SENHAS: lentas e ajustáveis de propósito. |
- 1992MD5 (RFC 1321)
Ronald Rivest publica o MD5, digest de 128 bits que domina a década.
- 1995SHA-1 (FIPS 180-1)
O NIST publica o SHA-1, de 160 bits, como sucessor mais robusto.
- 2004Colisões no MD5
Wang et al. anunciam colisões para o MD5 completo na CRYPTO 2004.
- 2015SHA-3 padronizado (FIPS 202)
O NIST aprova a família Keccak/SHA-3 em 5 de agosto; construção esponja.
- 2015Argon2 vence o PHC
A Password Hashing Competition elege o Argon2 como função de senha de referência (julho).
- 2017SHAttered
CWI e Google produzem a primeira colisão prática do SHA-1 (23 de fevereiro).
- 2020SHA-1 is a Shambles
Leurent e Peyrin demonstram colisão de prefixo escolhido por ~US$ 45 mil.
- 2021RFC 9106
A IETF padroniza o Argon2 para hashing de senha e proof-of-work (setembro).
Criptografia: reversível com chave
Criptografia embaralha o conteúdo de forma que só quem tem a chave o recupera. Na simétrica, a mesma chave cifra e decifra, é rápida e ideal para volume de dados; o AES é o exemplo. Na assimétrica, um par de chaves pública/privada permite que qualquer um cifre para você, mas só você decifre (RSA, curvas elípticas), o que resolve troca de chave e assinatura digital, a base do HTTPS. Um cuidado essencial: cifrar não é só esconder, é esconder de forma autenticada. O AES-GCM é um AEAD (criptografia autenticada) e, além do sigilo, produz uma etiqueta que detecta qualquer adulteração. E jamais use o modo ECB, que vaza a estrutura do texto, o caso do pinguim, no fim desta seção, mostra por quê.
- AEAD (AES-GCM)
- Criptografia autenticada: além de cifrar, gera uma etiqueta de integridade. A criptografia de texto usa AES-GCM de 256 bits com a chave derivada da sua senha por PBKDF2-HMAC-SHA-256.
- Assimétrica (RSA / ECDH)
- Par pública/privada. ECDH acerta uma chave sem trocá-la em claro; RSA e ECDSA assinam. Nenhum segredo precisa ser compartilhado antes.
- HMAC (RFC 2104)
- Hash com chave que prova integridade E autoria de uma mensagem. Imune a length extension por construção, por isso substitui hash(chave ‖ mensagem).
A criptografia de texto do site é um bom lugar para ver a diferença na prática: você digita uma senha, ela deriva a chave por PBKDF2 e cifra com AES-GCM; sem a senha, o texto cifrado é ruído. Se um sistema consegue mostrar o valor original de volta sem você digitar chave nenhuma, então aquilo não era criptografia, era encoding disfarçado. Um JWT ilustra a armadilha oposta: ele é assinado, não cifrado, e o payload é legível por qualquer um; o guia de JWT mostra por que confundir assinar com cifrar coloca dado sensível em lugar errado.
Senhas: hash lento com sal, nunca criptografia
O erro clássico é "criptografar a senha". Criptografia é reversível: se o servidor consegue voltar ao texto, um vazamento também consegue. O correto é guardar o hash da senha, para nunca guardar o valor real. Mas aqui está o ponto que separa o hash de senha do hash de arquivo: para um arquivo, você quer o hash rápido, porque só precisa verificar integridade em milissegundos. Para uma senha, "rápido" é justamente o defeito. Uma GPU calcula bilhões de SHA-256 por segundo; se o banco vaza, o atacante testa bilhões de palpites por segundo contra cada hash. O SHA-256 é ótimo para arquivo e péssimo para senha pelo mesmo motivo: velocidade.
A solução são funções feitas para senha: lentas de propósito, com um fator de custo ajustável e um sal aleatório por senha. O bcrypt (Provos e Mazières, 1999) foi o pioneiro; o scrypt (RFC 7914) e o Argon2 (RFC 9106, vencedor da Password Hashing Competition de 2015) são memory-hard, isto é, também exigem muita memória, o que estraga a vantagem das GPUs. O PBKDF2 (RFC 8018) é o mais antigo e só depende de iterações. A OWASP Password Storage Cheat Sheet recomenda hoje, como ponto de partida, os parâmetros da tabela:
| Função | Parâmetro de custo (mínimo) | Quando escolher |
|---|---|---|
| Argon2id | 19 MiB de memória, 2 iterações, paralelismo 1 | Primeira escolha para código novo. |
| scrypt | N = 2^17, r = 8, p = 1 | Alternativa memory-hard quando não há Argon2. |
| bcrypt | fator de trabalho 10+ (limite de 72 bytes) | Sistemas legados já com bcrypt. |
| PBKDF2-HMAC-SHA-256 | 600.000 iterações ou mais | Quando exige-se conformidade FIPS-140. |
- Sal (salt)
- Valor aleatório e único por senha, guardado junto do hash. Faz duas senhas iguais gerarem hashes diferentes e inutiliza tabelas rainbow pré-calculadas.
- Pimenta (pepper)
- Segredo global do servidor, misturado à senha antes do hash e guardado FORA do banco. Se só o banco vaza, o hash sozinho não basta para o ataque.
Segundo exemplo trabalhado, com o valor real: se dois usuários escolhem a senha senha123, o SHA-256 de ambos é exatamente 55a5e9e7…4e6cd251, idêntico. O atacante calcula esse digest uma vez, procura na lista de hashes vazados e quebra os dois de uma vez; e como senha123 já está em toda tabela rainbow, isso é instantâneo. Agora adicione um sal aleatório por usuário: os hashes ficam diferentes, a tabela pré-calculada não serve, e o Argon2 obriga o atacante a gastar memória e tempo por palpite. O sal não deixa a senha mais forte, ele destrói a economia de escala do ataque.
- Use Argon2id, scrypt ou bcrypt, nunca SHA-256 puro, para armazenar senha.
- Um sal aleatório por senha, guardado junto do hash.
- Nunca guarde senha em texto, Base64 ou MD5/SHA puro.
- Suba o fator de custo até o hash levar de 0,5 a 1 segundo no seu servidor.
O gerador de bcrypt mostra como fica um hash de senha com sal e fator de custo embutidos na própria string. E, antes de guardar qualquer senha, o guia como escolher e testar uma senha forte ajuda o usuário a não entregar de bandeja a entrada que o atacante mais quer.
Length extension: por que hash(chave ‖ mensagem) falha e o HMAC conserta
MD5, SHA-1 e a família SHA-2 usam a construção Merkle–Damgård: processam a mensagem em blocos e o hash final é, essencialmente, o estado interno da máquina. O problema aparece quando alguém autentica uma mensagem com hash(chave ‖ mensagem). Um atacante que conhece esse hash e o tamanho da mensagem pode retomar o cálculo a partir do estado final e produzir um hash válido para hash(chave ‖ mensagem ‖ padding ‖ extensão), sem saber a chave. É o length extension attack, e ele forja mensagens autenticadas.
A correção padrão é o HMAC (RFC 2104), que aninha dois hashes com a chave e não expõe o estado interno como saída. Por isso o HMAC existe: não é "mais um hash", é a forma certa de usar um hash com chave. As construções mais novas nem precisam dele para isso: o SHA-3, por ser esponja, e o BLAKE2 não sofrem length extension por projeto.
Por que SHA-256 é péssimo para senha e ótimo para arquivo
É a mesma propriedade, velocidade, vista de dois ângulos. Para verificar um download de 4 GB, você quer o hash o mais rápido possível: SHA-256 processa o arquivo em um piscar e confirma a integridade. Para guardar uma senha, você quer o hash o mais lento possível: cada milissegundo a mais multiplica o custo de um ataque de força bruta. Como o SHA-256 é otimizado para velocidade, ele entrega ao atacante bilhões de tentativas por segundo em hardware barato. Funções de senha invertem isso de propósito, com fator de custo e (no scrypt e no Argon2) exigência de memória.
Modo ECB e o pinguim: por que nunca cifrar em ECB
O AES cifra blocos de 16 bytes. No modo ECB (Electronic Codebook), cada bloco de texto claro é cifrado isoladamente, então blocos iguais viram blocos de cifra iguais. Cifre uma imagem com áreas de cor uniforme (o clássico é o pinguim Tux do Linux) em AES-ECB e o contorno do pinguim continua visível no resultado cifrado: a criptografia escondeu os bytes, mas preservou o padrão. É a demonstração mais famosa de que ECB não deve ser usado.
A saída é usar um modo com IV aleatório e, de preferência, autenticado: AES-GCM, como faz a ferramenta de criptografia deste site. Cada execução produz um texto cifrado diferente para a mesma entrada, e a etiqueta de autenticação detecta adulteração.
Perguntas frequentes
Dá para descriptografar um hash SHA-256?
Base64 é criptografia?
Por que não posso guardar senha com SHA-256?
O SHA-1 ainda é seguro?
Qual a diferença entre hash e HMAC?
Qual hash usar para verificar um arquivo?
Pergunte pela reversibilidade e pela chave: voltar sem segredo é encoding (transporte, não proteção); voltar só com a chave é criptografia (confidencialidade); não voltar de jeito nenhum é hashing (integridade). Senha nunca se criptografa, faz-se hash lento com sal em Argon2, scrypt ou bcrypt, porque o SHA-256 é rápido demais. E lembre que a colisão é a resistência mais frágil: foi ela que caiu no MD5 e no SHA-1.
Fontes e referências
- NIST FIPS 180-4, Secure Hash Standard (SHA-1, SHA-2)
- NIST FIPS 202, SHA-3 Standard (Keccak)
- The first collision for full SHA-1 (SHAttered), Stevens et al., 2017
- RFC 9106, Argon2 Memory-Hard Function for Password Hashing
- RFC 2104, HMAC: Keyed-Hashing for Message Authentication
- RFC 4648, The Base16, Base32, and Base64 Data Encodings
- OWASP, Password Storage Cheat Sheet