import bcrypt from 'bcrypt';
import argon2 from 'argon2';
async function verifyAndMigratePassword(password: string, storedHash: string, userId: string): Promise<boolean> {
const isLegacyBcrypt = storedHash.startsWith('$2b$') || storedHash.startsWith('$2a$');
if (isLegacyBcrypt) {
const isValid = await bcrypt.compare(password, storedHash);
if (isValid) {
// Rehash with Argon2id using safe 2026 parameters
const newHash = await argon2.hash(password, {
memoryCost: 65536, // 64MB
timeCost: 3,
parallelism: 4,
type: argon2.argon2id
});
await updateDatabaseUserHash(userId, newHash);
return true;
}
return false;
}
// If already migrated, verify with Argon2
return await argon2.verify(storedHash, password);
}Salutare. Hai să vorbim direct despre cum ne păstrăm parolele în baza de date acum, în 2026, fără să ne blocăm mașinile virtuale din cauza unui algoritm configurat prost. Am trecut recent prin asta la un proiect cu 120k useri activi și am tras câteva concluzii utile.
Dacă încă folosești Bcrypt cu cost factor de 10 doar pentru că "așa a venit boilerplate-ul", s-ar putea să ai o surpriză neplăcută la un audit de securitate sau, mai rău, la un leak de bază de date.
De ce Bcrypt își arată vârsta?
Bcrypt a fost excelent și încă își face treaba dacă e configurat cum trebuie. Totuși, are două limite mari. Prima este limita de 72 de caractere pentru parolă. Orice depășește această lungime este pur și simplu ignorat de algoritm. A doua problemă este că Bcrypt e optimizat pentru CPU, ceea ce înseamnă că atacatorii pot folosi GPU-uri sau cipuri ASIC customizate pentru a sparge hash-urile extrem de rapid prin brute force.
Argon2id (câștigătorul Password Hashing Competition) rezolvă asta prin faptul că este "memory-hard". Nu e vorba doar de puterea de procesare, ci și de lățimea de bandă a memoriei. Asta face atacurile pe GPU incredibil de scumpe și ineficiente.
Trade-off: Atenție la resursele din cloud
La Bcrypt e simplu: modifici un singur număr (cost factor). La un cost de 12, hashing-ul durează cam 250ms pe un server mediu. E un compromis bun între securitate și UX.
La Argon2id, ai trei parametri de care trebuie să tragi:
- m (memory): Câtă memorie RAM consumă o singură rulare.
- t (time/iterations): Câte iterații face peste memorie.
- p (parallelism): Câte thread-uri folosește.
Aici am pățit-o noi. Am setat inițial m=1048576 (1GB RAM) pe mediul de dev. Totul perfect. Când am urcat în staging, pe niște containere mai mici, primele teste de încărcare ne-au aruncat instant erori de Out Of Memory (OOM). Aplicația crăpa la fiecare 5 încercări de login simultane.
Pentru 2026, un profil de pornire sigur și realist pentru servere medii este: m=65536 (64MB), t=3, p=4. Oferă o rezistență excelentă fără să îți îngenuncheze infrastructura când ai un vârf de trafic.
Cum facem migrarea fără să deranjăm utilizatorii?
Evident, nu poți reseta parolele tuturor și nu le poți decripta pe cele vechi ca să le muți în noul format. Soluția este migrarea "on-the-fly", direct în fluxul de login.
Strategia e simplă. Când utilizatorul introduce parola:
- Verifici formatul hash-ului salvat în baza de date.
- Dacă e hash de tip vechi (începe cu
$2b$sau$2a$de la bcrypt), verifici parola folosind librăria de bcrypt. - Dacă parola e corectă, generezi imediat un hash nou folosind Argon2id.
- Salvezi noul hash în baza de date și finalizezi login-ul.
În felul acesta, în prima lună vei avea peste 80% din utilizatorii activi migrați pe noul algoritm în mod complet transparent. Cei inactivi rămân cu hash-ul vechi până la următoarea lor vizită.
Voi ce folosiți acum pe producție? Ați rămas pe Bcrypt din comoditate sau ați făcut deja pasul spre Argon2id?