⚖️ Comparativo Build·Desenvolvimento·13 min de leitura

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.

Vitor Morais

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

Hashing de senhas: comparativo técnico (recomendações OWASP 2026)
CritériobcryptArgon2idscryptPBKDF2
Ano de criação19992015 (PHC winner)20092000 (RFC 2898)
Configuração1 dim (cost)3 dim (mem, time, parallel)3 dim (N, r, p)1 dim (iterações)
Memory-hardParcialSimSimNão
Resistência GPURazoávelExcelenteBoaFraca
Limite de tamanho de senha72 bytesSem limiteSem limiteSem limite
Salt automáticoSimSimSimManual
Recomendação OWASP 2026Aceitável✅ PreferidoAceitávelSó com FIPS
Disponibilidade em libsUniversalBoa (cresceu)RazoávelUniversal

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, embutidos

Verificando 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:

Tempo alvo de hashing por contexto
CritérioTempo alvoJustificativa
App público sem fricção~250msLogin sem percepção de lentidão
App público padrão~500msEquilíbrio comum
Sistema crítico (banking, infra)1s+Vale o atraso pelo extra de proteção
Background batch2s+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:

  1. Adicione um campo password_algo (ou similar) no modelo de usuário.
  2. No login, identifique o algoritmo do hash armazenado e valide com a função correta.
  3. Em caso de sucesso, re-hashe a senha com Argon2id (ou bcrypt cost atualizado) e salve por cima.
  4. 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.

#bcrypt#argon2#argon2id#scrypt#pbkdf2#hashing#senhas#segurança#owasp#node.js

Continue lendo