Armazenamento seguro de senha
Nunca armazene senhas em texto puro. Nunca armazene apenas o hash MD5 ou SHA. A função de hash correta para senha é uma função de derivação de chave (KDF) lenta por design.
Por que MD5 e SHA não servem
MD5 e SHA-256 são rápidos — projetados para throughput alto. Uma GPU moderna calcula bilhões de hashes SHA-256 por segundo.
GPU RTX 4090:
SHA-256: ~22 bilhões de hashes/segundo
bcrypt (cost 12): ~2.800 hashes/segundo
Com MD5/SHA, um atacante que rouba o banco de dados testa senhas comuns em segundos via força bruta.
bcrypt
Algoritmo de 1999, ainda seguro. Inclui salt automático e fator de custo ajustável.
import bcrypt
# Hash de senha
senha = b"minha_senha_secreta"
hash_gerado = bcrypt.hashpw(senha, bcrypt.gensalt(rounds=12))
# $2b$12$... (salt + hash embutidos)
# Verificação
bcrypt.checkpw(senha, hash_gerado) # True
O parâmetro rounds=12 define o custo (2^12 iterações). Aumente conforme o hardware evolui.
argon2
Vencedor da Password Hashing Competition (2015). Recomendação atual do OWASP. Três variantes:
- Argon2d — resistente a GPU, mas vulnerável a side-channel.
- Argon2i — resistente a side-channel, menos a GPU.
- Argon2id — híbrido. Use este.
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost=3, # iterações
memory_cost=65536, # 64 MB de memória
parallelism=2
)
hash_gerado = ph.hash("minha_senha")
ph.verify(hash_gerado, "minha_senha") # True
O alto uso de memória torna ataques com hardware especializado (ASICs) inviáveis.
scrypt
Similar ao argon2 em conceito. Parametrizado por N (CPU/memória), r (tamanho de bloco) e p (paralelismo).
import hashlib, os
salt = os.urandom(16)
chave = hashlib.scrypt(b"senha", salt=salt, n=2**15, r=8, p=1)
Comparativo
| Algoritmo | Salt auto | Resistência GPU | Resistência ASIC | Recomendação OWASP |
|---|---|---|---|---|
| MD5 | Não | Fraco | Fraco | Nunca |
| SHA-256 | Não | Fraco | Fraco | Nunca (para senha) |
| bcrypt | Sim | Médio | Médio | Sim |
| scrypt | Não | Bom | Bom | Sim |
| argon2id | Sim | Excelente | Excelente | Preferido |
Regra de ouro
Ao verificar senha, compare sempre com função de tempo constante (checkpw, verify). Nunca compare strings diretamente — vulnerável a timing attack.