Guias de Segurança

Como armazenar senhas com segurança: a forma certa de fazer hash e salt

Por que texto puro, criptografia e hashes crus falham, o que faz o salt por usuário e como usar um hash lento (Argon2id, bcrypt, scrypt) e migrar.

Publicado 2026-06-27 Atualizado 2026-10-06 9 min de leitura
Nesta página

"Como exatamente devo armazenar as senhas dos usuários no banco de dados?" — todo construtor esbarra nessa pergunta uma vez. A resposta é clara. Aqui está a forma segura de armazená-las, em ordem, sem passos de ataque.

Por que texto puro, criptografia e hashes crus falham

Presuma que o banco de dados vai vazar algum dia. Quando vazar, o dano difere muito conforme o método de armazenamento.

  • Texto puro: toda senha é exposta no instante em que vaza. Pior, a reutilização de senhas encadeia a invasão para os outros serviços do usuário. O pior caso.
  • Criptografia (reversível): a chave a reverte — então se a chave vazar junto, você volta ao texto puro. Uma senha nunca precisa ser lida de volta, então ser reversível não traz benefício nenhum.
  • Hash cru (MD5/SHA-256): um hash rápido permite ao atacante testar tentativas rapidamente. Senhas comuns caem para rainbow tables e força bruta.

Quatro estágios até o armazenamento seguro

É mais rápido entender adicionando uma correção por vez a um método fraco.

1. texto puro: toda senha fica legível se vazar
↓ torne-o de mão única
2. hash cru: fraco contra rainbow tables
↓ adicione um salt por usuário
3. hash com salt: tabelas derrotadas, mas a força bruta ainda é rápida
↓ torne-o deliberadamente lento
4. hash com salt + lento (Argon2id / bcrypt): esta é a resposta
texto puro → hash cru → com salt → hash lento. Só no fim é realmente 'armazenamento seguro'.

A percepção-chave: um salt e um hash lento fazem trabalhos diferentes. Um salt derrota a pré-computação (rainbow tables) e a quebra em massa por reutilização. Um hash lento reduz a força bruta a uma taxa impraticável. Você precisa dos dois antes de estar seguro (→ o que é hashing).

Na prática: o que fazer agora

1

Use Argon2id (ou bcrypt)

Para sistemas novos, faça do Argon2id a primeira escolha — ele também pode exigir memória, o que resiste à força bruta por GPU. Se você prefere uma implementação bem testada, o bcrypt é mais do que suficiente na prática. Use a implementação da biblioteca padrão como está para qualquer um dos dois.

2

Deixe a biblioteca gerar o salt

bcrypt/Argon2 geram um salt por usuário e o embutem dentro da string de hash. Você não mantém uma coluna de salt separada. Montar à mão MD5(salt + senha) é o clássico que não se deve fazer.

3

Ajuste o custo para o seu ambiente

Defina o fator de custo do bcrypt, ou memória/iterações/paralelismo do Argon2, o mais alto que conseguir enquanto um login legítimo ainda parecer instantâneo. Conforme os servidores ficam mais rápidos, os atacantes também — revisite e eleve anualmente.

4

Verifique com a função de comparação padrão

No login, compare a entrada com o hash armazenado usando a função de verificação da biblioteca (a maioria usa uma comparação de tempo constante, segura contra timing). Não escreva sua própria igualdade de strings.

5

Migre hashes fracos refazendo o hash no login

Se você já armazena MD5/SHA-256, no momento em que um usuário faz login com sucesso, refaça o hash com o novo esquema e salve. Para usuários que não fizeram login, você pode reforçá-los enquanto isso aplicando o bcrypt sobre o hash existente (um hash em camadas).

Erros comuns vs. a implementação correta

Erro comum

  • Criptografar senhas para armazenar (chave vaza → acabou)
  • Armazenar MD5(senha) ou SHA-256(senha) diretamente
  • Usar um único salt compartilhado para todos os usuários
  • Montar à mão hash(salt + senha)

Implementação correta

  • Hash de mão única com Argon2id / bcrypt
  • Um salt por usuário (a biblioteca o adiciona automaticamente)
  • Custo o mais alto que parecer OK, elevado periodicamente
  • Usar as funções padrão de hash/verificação como estão

Hash vs criptografia vs codificação

Esses três costumam ser confundidos, mas diferem completamente em poder ou não ser revertidos. Só o hash é aceitável para armazenar senhas.

HashCriptografiaCodificação (Base64 etc.)
Reversível?Não (mão única)Sim, com a chaveSim, por qualquer pessoa
ChaveNenhumaObrigatóriaNenhuma
Uso típicoArmazenar senhas, detectar adulteraçãoManter dados ou tráfego confidenciaisMudar o formato dos dados
Para armazenar senhasUse (salt + hash lento)Não useNão use (equivale a texto puro)

Configurações recomendadas (OWASP)

As configurações mínimas do OWASP Password Storage Cheat Sheet. Trate-as como piso e eleve-as conforme o seu servidor.

AlgoritmoConfiguração mínimaQuando usar
Argon2id19 MiB de memória, 2 iterações, paralelismo 1Primeira escolha para sistemas novos
scryptN=2^17 (128 MiB), r=8, p=1Quando o Argon2id não está disponível
bcryptCusto 10 ou mais. Entrada limitada a 72 bytesSistemas existentes, histórico comprovado
PBKDF2-HMAC-SHA256600.000 iterações ou maisQuando a conformidade com o FIPS 140 é exigida

Verifique também o que a sua plataforma já traz. O password_hash() do PHP usa bcrypt por padrão (PASSWORD_DEFAULT), e o PHP 8.4 elevou o custo padrão de 10 para 12. O Django usa PBKDF2 por padrão; instale django[argon2] e coloque o Argon2 em primeiro lugar em PASSWORD_HASHERS para trocar. O Django refaz automaticamente o hash das senhas guardadas com um algoritmo antigo quando o usuário faz login.

Como verificar o seu próprio serviço

Normalmente dá para saber o esquema pelo começo da string de hash armazenada. Olhe algumas linhas num ambiente que você controla, sem copiar dados de produção para outro lugar.

Como o hash se pareceEsquemaAção
$argon2id$v=19$m=…,t=…,p=…$Argon2idConfira se m, t e p atendem ao mínimo da OWASP
$2y$12$… / $2b$10$…bcrypt (o número é o custo)Eleve se estiver abaixo de 10
pbkdf2_sha256$1000000$…PBKDF2 (o número é a quantidade de iterações)Confira se é pelo menos 600.000
32 caracteres hexadecimaisMuito provavelmente MD5Migre refazendo o hash no login
40 / 64 caracteres hexadecimaisMuito provavelmente SHA-1 / SHA-256Igual ao anterior
A string de login tal como foi digitadaTexto puroMigre primeiro. Avalie o impacto se tiver vazado

Verifique também o seguinte:

  • As senhas digitadas não ficam registradas em logs do app ou do servidor web, em relatórios de erro nem em ferramentas de analytics
  • Se você usa bcrypt, já decidiu como tratar entradas com mais de 72 bytes
  • Backups e cópias do banco de dados feitas para testes contêm só o mesmo tipo de hash

Exemplo real: o armazenamento em texto puro ampliou o dano

No incidente do Takufile-bin (2019), um acesso não autorizado a um servidor vazou cerca de 4,8 milhões de registros, e as senhas de login entre eles estavam guardadas em texto puro. Se estivessem guardadas com um hash lento com salt, o risco de as senhas vazadas serem usadas diretamente em outros serviços teria sido bem menor. Mesmo que você não consiga impedir toda invasão, a forma de armazenar senhas muda o tamanho do dano de um vazamento.

A visão deste site: para armazenar senhas, use a implementação padrão comprovada

Armazenamento de senha não é lugar de ser criativo. Usar as implementações padrão do Argon2/bcrypt, amplamente testadas, é o mais seguro e o mais fácil de operar. Nossa posição: não inove aqui. Gaste seu esforço, em vez disso, em reduzir a dependência de senhas em si — autenticação multifator (MFA) minuciosa e uma eventual migração para passkeys (sem senha). Nem o hash mais forte salva uma senha fraca e reutilizada.

Leia a seguir

Fontes

FAQ

QNão basta criptografar as senhas para armazenar?
A

Não — criptografia é a ferramenta errada. Criptografia é reversível com uma chave, então se a chave vazar, toda senha volta a texto puro. Uma senha nunca precisa ser lida de volta, nem por você, então um 'hash' irreversível é a resposta certa. Mas um hash cru não basta; combine um salt por usuário com um hash lento (bcrypt/Argon2).

Qbcrypt ou Argon2 — qual usar?
A

Para desenvolvimento novo, o Argon2 (Argon2id especialmente) é a primeira escolha: permite ajustar memória e processamento, o que resiste bem à força bruta por GPU. Se você valoriza uma implementação consagrada e bem testada, o bcrypt ainda é perfeitamente prático. O ponto-chave em qualquer um é usar a implementação da biblioteca padrão como está e não inventar a sua.

QEu já armazenei senhas com MD5/SHA-256. Como migro?
A

Você não consegue converter em massa de volta para texto puro (esse é o ponto), então migre no login. No momento em que um usuário faz login com sucesso, refaça o hash da senha correta que ele acabou de digitar com o novo esquema (ex.: Argon2id) e salve. Para usuários que não fizeram login, você pode reforçá-los enquanto isso aplicando o bcrypt sobre o hash existente (um hash em camadas).

QQual a diferença entre um salt e um pepper?
A

Um salt é um valor aleatório diferente para cada usuário, guardado no banco de dados junto com o hash. Ele não precisa ser secreto. Um pepper é um único valor secreto compartilhado por todos os usuários, mantido fora do banco de dados (num gerenciador de segredos ou num HSM, por exemplo). A OWASP descreve o pepper como uma defesa extra para o caso de só o banco de dados vazar. O salt é obrigatório; o pepper é um acréscimo opcional.

QO que é o limite de 72 bytes do bcrypt?
A

O bcrypt usa só os primeiros 72 bytes da entrada; tudo depois do byte 72 é ignorado. Caracteres multibyte (japonês, emoji) ocupam cerca de 3 a 4 bytes cada, então o limite chega antes do que se imagina. Para sistemas novos, escolha o Argon2id, que não tem esse limite; se usar bcrypt, defina um tamanho máximo de entrada (OWASP).

QO salt precisa ser mantido em segredo?
A

Não. A função do salt é fazer a mesma senha gerar um hash diferente para cada usuário, o que derrota tabelas pré-calculadas e a quebra em massa. Ele não precisa ser secreto, e o bcrypt e o Argon2 o guardam dentro da string de hash. O NIST SP 800-63B-4 exige salts de pelo menos 32 bits.