Ce face crypto.scrypt (parole)
Parolele nu se salvează niciodată ca text și nici ca hash simplu (SHA-256), pentru că acestea se calculează foarte repede și pot fi încercate miliarde de variante pe secundă. scrypt este o funcție gândită să fie lentă și să consume memorie, ceea ce face ghicirea mult mai scumpă.
Pentru fiecare utilizator generezi o sare aleatorie (randomBytes(16)) și o salvezi lângă rezultat. Sarea face ca doi utilizatori cu aceeași parolă să aibă rezultate diferite. La autentificare, calculezi din nou scrypt cu parola introdusă și sarea salvată și compari cu timingSafeEqual.
Costul se reglează prin opțiunile N (implicit 16384, putere a lui 2), r și p. Varianta scryptSync blochează firul principal pe durata calculului; într-un server folosește varianta asincronă (cu util.promisify).
Parametri
| Parametru | Descriere |
|---|---|
parolă |
Parola, ca șir sau Buffer. |
sare |
Valoare aleatorie unică per utilizator, de cel puțin 16 octeți. |
lungimeCheie |
Câți octeți să aibă rezultatul (de obicei 32 sau 64). |
opțiuni.N, r, p |
Costul de calcul și de memorie. |
Ce returnează
Un Buffer cu cheia derivată (prin callback sau, la scryptSync, direct).
Exemple
Exemplul 1: Salvare și verificare parolă
La înregistrare, sarea se generează cu randomBytes(16). Aici este fixă, ca rezultatul să fie același la fiecare rulare.
import { scrypt, timingSafeEqual } from "node:crypto";
import { promisify } from "node:util";
const scryptAsync = promisify(scrypt);
async function hashParola(parola, sare) {
const cheie = await scryptAsync(parola, sare, 32);
return `${sare.toString("hex")}:${cheie.toString("hex")}`;
}
async function verificaParola(parola, salvat) {
const [sareHex, cheieHex] = salvat.split(":");
const cheie = await scryptAsync(parola, Buffer.from(sareHex, "hex"), 32);
return timingSafeEqual(cheie, Buffer.from(cheieHex, "hex"));
}
const sare = Buffer.from("a1b2c3d4e5f60718293a4b5c6d7e8f90", "hex"); // în realitate: randomBytes(16)
const salvat = await hashParola("Iasi2026!", sare);
console.log(salvat);
console.log(await verificaParola("Iasi2026!", salvat));
console.log(await verificaParola("iasi2026!", salvat));
Rezultat
a1b2c3d4e5f60718293a4b5c6d7e8f90:29a9d7695fef23bbb8bfa69d19478e6f57497fc5a12bbdd3588dc4b3c8495986
true
false
Exemplul 2: Aceeași parolă, sare diferită
import { scryptSync } from "node:crypto";
const a = scryptSync("parola123", "sare-ana", 16).toString("hex");
const b = scryptSync("parola123", "sare-ion", 16).toString("hex");
console.log(a);
console.log(b);
console.log("identice:", a === b);
Rezultat
73916c0d937b659443e8d9738adcaa07
e05a4cd0d5bd62778a3a27f4b2340981
identice: false
Greșeli frecvente
- Folosești aceeași sare pentru toți utilizatorii (sau deloc): parolele identice dau rezultate identice, iar un atacator le sparge pe toate odată.
- Compari cheile cu
===pe textul hex: foloseștetimingSafeEqualpeBuffer-e de aceeași lungime. - Folosești
scryptSyncla fiecare cerere de autentificare dintr-un server: blochează serverul zeci de milisecunde de fiecare dată.
Comenzi înrudite
crypto.randomBytes / randomUUID / randomInt: Valori aleatorii sigure pentru criptografie: octeți pentru chei și token-uri, UUID-uri, numere întregi într-un interval.crypto.createHash / createHmac: Calculează amprenta (hash) unui text sau fișier cu SHA-256 și semnături HMAC pentru verificarea mesajelor.util.promisify: Transformă o funcție cu callback în stil Node.js (eroare, rezultat) într-o funcție care întoarce o promisiune.