bcrypt vs Argon2 vs scrypt vs PBKDF2: Qual Usar para Senhas
Comparativo técnico OWASP-aligned dos quatro principais algoritmos de hashing de senhas em 2026. Veja recomendações, parâmetros corretos por hardware, exemplos prontos em 4 linguagens e estratégia de migração de algoritmos legados.
Por Vitor Morais
Fundador do MochaLabz ·
Gere senhas com alta entropia
Senhas fortes para usar com bcrypt ou Argon2id — geração 100% no navegador.
Usar gerador de senha →Armazenar senha em texto puro é falha grave. Usar MD5 ou SHA-256 puro também — esses algoritmos foram projetados para velocidade, não para resistir a brute force. O padrão correto em 2026 são algoritmos memory-hard ou configurably slow: bcrypt, Argon2id, scrypt e PBKDF2. Cada um tem cenário ideal — esta comparação ajuda você a escolher e configurar corretamente.
Por que MD5 e SHA não servem para senhas
MD5 e SHA-256 foram projetados para ser rápidos — processar gigabytes por segundo. Isso é ótimo para checksums e assinaturas digitais, mas catastrófico para senhas. Uma única GPU moderna testa entre 10 e 100 bilhões de hashes MD5/SHA por segundo. Com hardware especializado (FPGA/ASIC), chega a trilhões.
Resultado prático: uma base de senhas vazada com hash MD5/SHA puro é quebrada em horas para senhas curtas, dias para senhas médias. Salt ajuda contra rainbow tables prontas, mas não muda o fato de que cada hash individual cai rápido.
Anti-padrões clássicos
md5(password)— quebrado em segundos.sha256(password)— quebrado em minutos.sha256(salt + password)— protege contra rainbow tables, mas ainda é rápido demais.- Pepper estático sem rotação — atacante que tem o pepper quebra tudo.
- Reusar o mesmo salt para todos os usuários — anula completamente a proteção.
O que torna um algoritmo bom para senhas
Um algoritmo de hashing para senhas precisa ter três propriedades:
- Lentidão configurável: via cost factor (bcrypt), iterações (PBKDF2) ou memória/tempo (Argon2id, scrypt). Você ajusta para que cada hash leve um tempo perceptível (~250ms).
- Salt automático e único: o algoritmo gera salt criptograficamente forte e o embute no hash final. Cada usuário tem salt distinto, anulando rainbow tables.
- Resistência a hardware paralelo: uso de memória (memory-hard) impede que GPUs e ASICs tirem vantagem do paralelismo. Argon2id e scrypt brilham aqui; bcrypt resiste razoavelmente; PBKDF2 não resiste.
Comparativo dos 4 principais algoritmos
| Critério | bcrypt | Argon2id | scrypt | PBKDF2 |
|---|---|---|---|---|
| Ano de criação | 1999 | 2015 (PHC winner) | 2009 | 2000 (RFC 2898) |
| Configuração | 1 dim (cost) | 3 dim (mem, time, parallel) | 3 dim (N, r, p) | 1 dim (iterações) |
| Memory-hard | Parcial | Sim | Sim | Não |
| Resistência GPU | Razoável | Excelente | Boa | Fraca |
| Limite de tamanho de senha | 72 bytes | Sem limite | Sem limite | Sem limite |
| Salt automático | Sim | Sim | Sim | Manual |
| Recomendação OWASP 2026 | Aceitável | ✅ Preferido | Aceitável | Só com FIPS |
| Disponibilidade em libs | Universal | Boa (cresceu) | Razoável | Universal |
bcrypt: o padrão testado pelo tempo
Criado em 1999 baseado no cipher Blowfish, bcrypt usa um cost factor (também chamado work factor ou rounds). Cada incremento dobra o tempo de processamento — cost 12 leva o dobro do tempo do cost 11.
// Node.js
import bcrypt from 'bcrypt';
const COST = 12; // OWASP 2026: mínimo 10, ideal 12
// Hash ao cadastrar
async function hashPassword(password) {
return bcrypt.hash(password, COST);
}
// Verificar ao fazer login
async function verifyPassword(password, hash) {
return bcrypt.compare(password, hash);
}
const hash = await hashPassword('MinhaSenh@123');
// "$2b$12$N9qo8uLOickgx2ZMRZoMye..."
// │ │
// │ └─ cost factor (12)
// └───── versão do algoritmo (2b)
// o salt e o hash vêm a seguir, embutidosVerificando o cost antigo
bcrypt embute o cost no próprio hash. Para detectar usuários com cost antigo e re-hashear no login: leia o cost atual do hash armazenado, compare com o cost desejado e re-hashe se estiver desatualizado.
Argon2id: o vencedor da Password Hashing Competition
Argon2 venceu a Password Hashing Competition em 2015. Tem três variantes: Argon2d (resistente a GPU, mas vulnerável a side-channel), Argon2i (resistente a side-channel, mas mais lento) e Argon2id (híbrido — recomendado para senhas).
// Node.js
import argon2 from 'argon2';
// Parâmetros mínimos OWASP 2026
const OPTIONS = {
type: argon2.argon2id,
memoryCost: 19456, // 19 MiB
timeCost: 2, // iterações
parallelism: 1,
};
async function hashPassword(password) {
return argon2.hash(password, OPTIONS);
}
async function verifyPassword(hash, password) {
return argon2.verify(hash, password);
}
const hash = await hashPassword('MinhaSenh@123');
// "$argon2id$v=19$m=19456,t=2,p=1$..."
// │ │ │
// │ │ └─ paralelismo
// │ └───── time cost
// └──────────── memory cost (KiB)Os três parâmetros permitem calibrar para cada perfil de servidor: aumente memoryCost em servidores com RAM sobrando; aumente timeCost se a memória for limitada; ajuste parallelism ao número de cores disponíveis.
scrypt: alternativa memory-hard
Criado por Colin Percival (2009), scrypt foi o primeiro memory-hard popular. Continua bom — usado pelo Node.js nativo (crypto.scrypt) e por carteiras Bitcoin antigas. Mas perdeu espaço para Argon2id em projetos novos pela flexibilidade menor.
import { scryptSync, randomBytes, timingSafeEqual } from 'crypto';
function hashPassword(password) {
const salt = randomBytes(16);
// N=2^17 (cost), r=8 (block size), p=1 (parallelism)
const hash = scryptSync(password, salt, 64, { N: 2 ** 17 });
return `${salt.toString('hex')}:${hash.toString('hex')}`;
}
function verifyPassword(password, stored) {
const [saltHex, hashHex] = stored.split(':');
const salt = Buffer.from(saltHex, 'hex');
const hash = Buffer.from(hashHex, 'hex');
const test = scryptSync(password, salt, 64, { N: 2 ** 17 });
return timingSafeEqual(hash, test);
}PBKDF2: aceitável só com FIPS
PBKDF2 (RFC 2898) é o algoritmo mais antigo dos quatro e o único aprovado pelo FIPS 140-2 — relevante para órgãos governamentais norte-americanos e empresas reguladas. Não é memory-hard, então oferece menos proteção contra GPUs/ASICs. Use só se houver requisito explícito de compliance.
import { pbkdf2Sync, randomBytes } from 'crypto';
const ITERATIONS = 600_000; // OWASP 2026 mínimo para SHA-256
const KEY_LEN = 32;
const ALGORITHM = 'sha256';
function hashPassword(password) {
const salt = randomBytes(16);
const hash = pbkdf2Sync(password, salt, ITERATIONS, KEY_LEN, ALGORITHM);
return `pbkdf2_sha256$${ITERATIONS}$${salt.toString('hex')}$${hash.toString('hex')}`;
}Implementação em outras linguagens
Python (Argon2id)
# pip install argon2-cffi
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher(
time_cost=2,
memory_cost=19456,
parallelism=1,
)
hash_ = ph.hash("MinhaSenh@123")
try:
ph.verify(hash_, "MinhaSenh@123") # OK
if ph.check_needs_rehash(hash_):
hash_ = ph.hash("MinhaSenh@123")
except VerifyMismatchError:
print("senha inválida")Go (Argon2id)
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"golang.org/x/crypto/argon2"
)
func HashPassword(password string) (string, error) {
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
return "", err
}
// time=2, memory=19MiB, threads=1, keyLen=32
hash := argon2.IDKey([]byte(password), salt, 2, 19*1024, 1, 32)
return base64.RawStdEncoding.EncodeToString(salt) + "$" +
base64.RawStdEncoding.EncodeToString(hash), nil
}PHP (built-in password_hash)
<?php
// PHP 7.2+ — usa Argon2i; PHP 7.3+ permite Argon2id
$hash = password_hash($senha, PASSWORD_ARGON2ID, [
'memory_cost' => 19456,
'time_cost' => 2,
'threads' => 1,
]);
if (password_verify($senha, $hash)) {
if (password_needs_rehash($hash, PASSWORD_ARGON2ID, $opts)) {
$hash = password_hash($senha, PASSWORD_ARGON2ID, $opts);
}
}Como escolher cost factor / parâmetros
Não existe configuração universal. Calibre no servidor de produção visando ~250ms por hash — equilíbrio entre proteção e UX no login. A regra prática:
| Critério | Tempo alvo | Justificativa |
|---|---|---|
| App público sem fricção | ~250ms | Login sem percepção de lentidão |
| App público padrão | ~500ms | Equilíbrio comum |
| Sistema crítico (banking, infra) | 1s+ | Vale o atraso pelo extra de proteção |
| Background batch | 2s+ | Sem usuário esperando |
Calibração rápida
Rode o algoritmo no servidor de produção em loop de 100 iterações. Pegue a média. Se está em 80ms, suba o cost (ou memory) até chegar perto de 250ms. Reavalie a cada 12–24 meses, conforme o hardware fica mais rápido.
Estratégia de migração de algoritmos antigos
Se você tem usuários em MD5, SHA-256 puro ou bcrypt com cost baixo, migrar é seguro com este padrão:
- Adicione um campo
password_algo(ou similar) no modelo de usuário. - No login, identifique o algoritmo do hash armazenado e valide com a função correta.
- Em caso de sucesso, re-hashe a senha com Argon2id (ou bcrypt cost atualizado) e salve por cima.
- Após 6–12 meses, force reset dos usuários inativos remanescentes.
async function login(email, senha) {
const user = await db.users.findByEmail(email);
let valido = false;
if (user.password_algo === 'md5') {
valido = md5(senha) === user.password_hash;
} else if (user.password_algo === 'bcrypt') {
valido = await bcrypt.compare(senha, user.password_hash);
} else if (user.password_algo === 'argon2id') {
valido = await argon2.verify(user.password_hash, senha);
}
if (!valido) throw new Error('credenciais inválidas');
// Migra silenciosamente para Argon2id
if (user.password_algo !== 'argon2id') {
user.password_hash = await argon2.hash(senha, ARGON2_OPTS);
user.password_algo = 'argon2id';
await db.users.save(user);
}
return createSession(user);
}O que mais blindar além do hash
- Rate limiting: bloqueie tentativas excessivas por IP e por conta — sem isso, mesmo bcrypt cai por força bruta.
- MFA: Two-factor remove a senha como single point of failure.
- HIBP API: rejeite senhas que aparecem em vazamentos conhecidos (haveibeenpwned.com).
- Logging seguro: nunca logue a senha, nem mesmo em debug temporário.
- Pepper: opcional — string secreta concatenada antes do hash, guardada fora do banco. Adiciona camada se a base vazar.
Senhas longas e aleatórias (16+ caracteres de alta entropia) funcionam como input ideal para esses algoritmos: o custo computacional do hash somado à imprevisibilidade da senha torna o ataque por força bruta inviável.
Veredicto
Resumo executivo
- Projeto novo: Argon2id com parâmetros OWASP 2026.
- Projeto existente em bcrypt: manter, garantir cost ≥ 12, migrar gradual no login.
- Compliance FIPS: PBKDF2 com 600k+ iterações.
- Em qualquer caso: rate limiting, MFA, HIBP check, calibrar para ~250ms por hash.
Checklist final
- ✅ Algoritmo é Argon2id (preferido) ou bcrypt cost ≥ 12.
- ✅ Salt único e gerado por aleatoriedade criptográfica.
- ✅ Tempo de hash calibrado em ~250ms na produção.
- ✅ Re-hash automático no login quando algoritmo/cost ficou desatualizado.
- ✅ Verificação contra Have I Been Pwned no cadastro.
- ✅ Rate limiting agressivo no login.
- ✅ MFA disponível para todos os usuários.
- ✅ Senhas nunca aparecem em logs ou queries.
- ✅ Plano de rotação de cost/parâmetros a cada 12–24 meses.
Perguntas frequentes
bcrypt ou Argon2id em 2026?+
Para projetos novos, Argon2id é a recomendação oficial da OWASP desde 2021. Ele é resistente tanto a ataques de GPU quanto a side-channel, é configurável em três dimensões (memória, tempo, paralelismo) e não tem o limite de 72 bytes do bcrypt. Para projetos existentes em bcrypt com cost factor 12+, manter é seguro — migre só quando a próxima rotação de senha permitir.
Por que MD5 ou SHA-256 não servem para senhas?+
Eles foram projetados para serem rápidos (processar GBs por segundo). Atacantes com GPUs modernas testam bilhões de hashes MD5/SHA por segundo, o que torna qualquer base de senhas vazada quebrável em horas. Algoritmos para senhas (bcrypt, Argon2id, scrypt, PBKDF2) são intencionalmente lentos e configuravelmente caros.
Qual cost factor de bcrypt usar em 2026?+
OWASP recomenda mínimo cost 10 em 2026; ideal 12 para a maioria dos servidores; 13–14 para sistemas críticos com hardware bom. Cada incremento dobra o tempo. Cost 12 leva ~250ms num servidor moderno típico — bom equilíbrio entre segurança e UX. Reavalie a cada 2 anos: hardware fica mais rápido, e o cost também precisa subir.
Quais parâmetros usar com Argon2id?+
OWASP 2026 recomenda: memoryCost = 19 MiB (19456), timeCost = 2, parallelism = 1 como base mínima. Para sistemas com mais hardware: memoryCost = 47 MiB (48128), timeCost = 1, parallelism = 1. Calibre rodando o algoritmo no servidor de produção e ajuste para que cada hash leve entre 250ms e 1s — equilíbrio entre proteção contra brute force e UX no login.
Preciso usar salt manualmente?+
Não. bcrypt, Argon2 e scrypt já geram e embutem o salt automaticamente no hash final — você nunca precisa manipular salt manualmente. PBKDF2 exige passar o salt explicitamente. Em todos os casos, o salt deve ser único por usuário (nunca reuse) e gerado com aleatoriedade criptográfica (16 bytes mínimo).
bcrypt tem limite de 72 caracteres na senha?+
Sim — bcrypt trunca silenciosamente a senha em 72 bytes. Para senhas/passphrases muito longas, isso é problema. Solução comum: pre-hashear com SHA-256 e passar o resultado em base64 para o bcrypt. Argon2id e scrypt não têm esse limite. Para projetos novos com possibilidade de passphrases longas, prefira Argon2id.
Como migrar usuários de MD5/SHA antigos para bcrypt ou Argon2?+
Estratégia em duas fases: (1) marque cada hash com a versão do algoritmo (campo extra no banco); (2) no login, se o hash é antigo, valide com o algoritmo antigo e em caso de sucesso re-hashe com o algoritmo novo, salvando por cima. Em poucos meses a maioria dos usuários ativos migra automaticamente — depois disso, force reset dos restantes.
scrypt e PBKDF2 ainda fazem sentido?+
PBKDF2 é aceitável quando há restrição regulatória (FIPS 140-2 ainda exige) ou em ambientes muito restritivos sem libs de bcrypt/Argon2 disponíveis — use no mínimo 600 000 iterações com SHA-256 em 2026. scrypt é uma boa opção memory-hard mas menos popular que Argon2; perde para Argon2id em flexibilidade de configuração. Para novos projetos sem restrição, Argon2id continua sendo a melhor escolha.
Continue lendo
O que é UUID? Guia Completo com Exemplos
Entenda o que são UUIDs, os diferentes tipos (v1, v4, v7) e quando usar cada um em seus projetos.
Regex para Validação de Formulários (2026): E-mail, CPF, Telefone, CEP
Coleção de regex prontas para campos brasileiros: e-mail, CPF, CNPJ, telefone, CEP, URL, data, senha forte, cartão. Inclui máscara em tempo real, integração React e ReDoS.
JWT e Base64URL: Como Funciona a Autenticação com JSON Web Tokens (2026)
Anatomia completa do JWT, papel do Base64URL, decodificação manual em qualquer linguagem, escolha entre HS256/RS256/ES256, claims padrão, boas práticas de armazenamento e os 8 erros que destroem a segurança.
LGPD para Sites: Checklist Completo de Adequação (2026)
Guia LGPD para sites brasileiros: 30+ itens de checklist, bases legais explicadas, multas reais aplicadas pela ANPD, prioridade de implementação por fase e ferramentas recomendadas.