import bcrypt from 'bcrypt';
import argon2 from 'argon2';
async function verifyAndMigratePassword(password: string, storedHash: string, userId: string): Promise<boolean> {
// 1. Verificăm dacă hash-ul este de tip Argon2id
if (storedHash.startsWith('$argon2id$')) {
return await argon2.verify(storedHash, password);
}
// 2. Dacă este un hash vechi de tip bcrypt
if (storedHash.startsWith('$2b$') || storedHash.startsWith('$2a$')) {
const isValid = await bcrypt.compare(password, storedHash);
if (isValid) {
// Migrare on-the-fly: generăm hash-ul nou și îl salvăm asincron
const newHash = await argon2.hash(password, {
memoryCost: 65536,
timeCost: 3,
parallelism: 4
});
await db.users.updateHash(userId, newHash);
}
return isValid;
}
return false;
}Încă văd proiecte noi pornite direct cu bcrypt și costul default de 10, fără ca cineva să se gândească la resurse sau securitate reală. Dacă ai o aplicație cu peste 10k utilizatori, e momentul să schimbi abordarea. În 2026, puterea de calcul accesibilă atacatorilor a crescut masiv, iar deciziile luate acum cinci ani s-ar putea să te coste scump.
Hai să vedem de ce bcrypt începe să își arate vârsta, cum configurăm corect Argon2id și cum facem o migrare transparentă pentru utilizatori, fără să le resetăm parolele.
De ce bcrypt își arată limitele
Bcrypt a fost standardul de aur mulți ani și încă e decent dacă e configurat corect. Problema lui principală este că e conceput să fie costisitor doar pentru CPU, folosind extrem de puțină memorie (în jur de 4KB).
Pentru un atacator care folosește plăci video moderne sau cipuri ASIC dedicate, lipsa necesarului de memorie este un vis. Pot paraleliza atacul la un nivel incredibil. În plus, bcrypt are o limită dură de 72 de caractere pentru parolă; tot ce trece de această lungime este pur și simplu ignorat.
La un proiect anterior, cu vreo 80k useri activi, aveam bcrypt cu cost 12. Generarea unui hash dura cam 280ms pe un container destul de slab. Când am avut un spike de login-uri de la un newsletter, procesorul containerului a stat în 100% timp de 10 minute, blocând alte request-uri legitime.
Argon2id: Avantaje și compromisuri reale
Argon2id este câștigătorul competiției Password Hashing Competition și este recomandat de OWASP. Spre deosebire de bcrypt, Argon2id este "memory-hard". Adică folosește în mod intenționat memorie RAM pe lângă CPU, ceea ce face atacurile hardware masiv paralelizate extrem de scumpe și ineficiente.
Dar vine cu un trade-off sincer: dacă ai containere mici în cloud (de exemplu, instanțe cu 512MB RAM în Kubernetes), configurarea greșită a Argon2id îți poate pune serverul în cap foarte ușor. Trebuie să găsești echilibrul corect între memorie, timp și paralelism.
În producție, o configurație sigură și destul de blândă cu resursele în 2026 arată cam așa:
- Memorie (m): 65536 KB (64 MB)
- Iterații (t): 3
- Paralelism (p): 4 (ajustează în funcție de numărul de nuclee disponibile)
Cu setările astea, o verificare ar trebui să dureze în jur de 150-200ms. Dacă ai resurse limitate pe server, poți coborî memoria la 32MB, dar nu mai jos.
Strategia de migrare graduală (on-the-fly)
Cum treci de la bcrypt la Argon2id fără să obligi utilizatorii să își schimbe parolele la următorul login?
Strategia este simplă și am aplicat-o cu succes de mai multe ori. Nu facem nicio migrare bulk în baza de date (pentru că nu avem parolele în clar). În schimb, facem migrarea direct în fluxul de login.
Când utilizatorul încearcă să se autentifice:
- Verificăm ce fel de hash avem stocat în baza de date.
- Dacă hash-ul începe cu
$2b$sau$2a$, știm că este bcrypt. Verificăm parola folosind biblioteca de bcrypt. - Dacă parola este corectă, generăm imediat un hash nou folosind Argon2id.
- Salvăm noul hash în baza de date în locul celui vechi.
- Dacă hash-ul începe cu
$argon2id$, rulăm direct verificarea cu Argon2id.
În felul acesta, în câteva săptămâni, toți utilizatorii activi vor fi migrați automat la noul algoritm, fără nicio fricțiune.
Ce facem cu conturile inactive? Le lăsăm așa. Dacă un utilizator revine după doi ani, migrarea lui se va face instant în momentul în care introduce parola corectă.
Voi ce folosiți pe proiectele curente? Ați făcut deja trecerea la Argon2id sau bcrypt cu un cost factor ridicat este încă suficient pentru ce aveți nevoie?