{"id":82,"date":"2026-06-12T16:30:08","date_gmt":"2026-06-12T16:30:08","guid":{"rendered":"https:\/\/shattered.io\/it\/2026\/06\/12\/hashing-password-bcrypt-nodejs\/"},"modified":"2026-06-12T16:31:29","modified_gmt":"2026-06-12T16:31:29","slug":"hashing-password-bcrypt-nodejs","status":"publish","type":"post","link":"https:\/\/shattered.io\/it\/2026\/06\/12\/hashing-password-bcrypt-nodejs\/","title":{"rendered":"Hashing Password con bcrypt in Node.js: 12 Step [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Ogni volta che un database di credenziali finisce online, la differenza tra un incidente gestibile e un disastro dipende da una scelta tecnica fatta mesi prima: come sono state archiviate le password. Salvarle in chiaro, o con un semplice MD5, significa regalare gli account agli attaccanti nel giro di minuti. Usare <strong>bcrypt<\/strong> con un cost factor adeguato significa costringerli a una battaglia di forza bruta che, nella pratica, non vincono. Questo tutorial mostra passo dopo passo come implementare l&#8217;hashing delle password con bcrypt in Node.js, dalla prima riga di codice fino a una API di autenticazione completa e pronta per la produzione.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Seguirai 12 step concreti, con codice testato, esempi di output reali e una sezione di troubleshooting per gli errori pi\u00f9 frequenti. Alla fine avrai un progetto Express funzionante che registra utenti, verifica i login in modo sicuro, gestisce il limite dei 72 byte di bcrypt e ricalcola automaticamente gli hash quando l&#8217;hardware diventa pi\u00f9 potente. Tempo stimato: circa 30 minuti.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"perche-serve-bcrypt-nel-2026-e-perche-non-basta-una-hash-function\">Perch\u00e9 serve bcrypt nel 2026 (e perch\u00e9 non basta una hash function)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Una funzione hash crittografica come SHA-256 \u00e8 progettata per essere veloce. \u00c8 esattamente la propriet\u00e0 sbagliata per le password. Una GPU moderna calcola miliardi di hash SHA-256 al secondo, quindi un attaccante che ruba un database di hash SHA-256 pu\u00f2 testare interi dizionari e liste di password trapelate in pochi minuti. Le password non vanno trattate come dati da indicizzare, vanno trattate come segreti da rendere costosi da indovinare.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bcrypt risolve il problema con un approccio deliberatamente lento e adattivo. Si basa sul cifrario Blowfish e introduce un parametro di costo (il cost factor) che determina quante iterazioni il calcolo deve eseguire. Il costo cresce in modo esponenziale: ogni incremento di un&#8217;unit\u00e0 raddoppia il tempo di calcolo, perch\u00e9 il lavoro \u00e8 pari a 2 elevato al valore del cost. Questo rende l&#8217;hashing abbastanza veloce per un login legittimo (frazioni di secondo) ma proibitivo per chi prova miliardi di combinazioni.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La parola chiave \u00e8 &#8220;adattivo&#8221;. bcrypt esiste dal 1999, ma \u00e8 ancora raccomandato dall&#8217;OWASP nel 2026 proprio perch\u00e9 il cost factor si pu\u00f2 aumentare nel tempo. Quando, fra cinque anni, le GPU saranno il doppio pi\u00f9 veloci, ti baster\u00e0 alzare il costo di un&#8217;unit\u00e0 per riportare la difesa al punto di partenza. Nessun&#8217;altra propriet\u00e0 di sicurezza invecchia cos\u00ec bene.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bcrypt incorpora inoltre un salt univoco e casuale dentro ogni hash. Il salt impedisce gli attacchi con rainbow table (tabelle precalcolate) e garantisce che due utenti con la stessa password ottengano hash completamente diversi. Non devi gestire il salt separatamente: la libreria lo genera, lo usa e lo memorizza dentro la stringa finale. \u00c8 uno dei motivi per cui bcrypt resta cos\u00ec semplice da usare correttamente.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisiti-e-versioni-necessarie\">Prerequisiti e versioni necessarie<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prima di scrivere codice, assicurati di avere l&#8217;ambiente corretto. Questo tutorial usa la libreria nativa <code>bcrypt<\/code> (binding C++ ad alte prestazioni) e, in alternativa, accenna a <code>bcryptjs<\/code> per gli ambienti dove non puoi compilare moduli nativi. La tabella seguente riassume cosa ti serve.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Componente<\/th><th>Versione consigliata<\/th><th>Note<\/th><\/tr><\/thead><tbody><tr><td>Node.js<\/td><td>20 LTS o 22 LTS<\/td><td>Necessario per il binding nativo di bcrypt<\/td><\/tr><tr><td>npm<\/td><td>10 o superiore<\/td><td>Incluso con Node.js<\/td><\/tr><tr><td>bcrypt<\/td><td>6.0.0 (ultima versione)<\/td><td>Circa 4,8 milioni di download settimanali<\/td><\/tr><tr><td>bcryptjs<\/td><td>ultima versione<\/td><td>Pura JavaScript, zero dipendenze, per serverless<\/td><\/tr><tr><td>express<\/td><td>4.x o 5.x<\/td><td>Per la API di esempio degli step 7-9<\/td><\/tr><tr><td>Build tools<\/td><td>Python 3 + compilatore C<\/td><td>Solo per il pacchetto nativo bcrypt<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Verifica la tua versione di Node.js prima di iniziare. Un binding nativo come bcrypt richiede una versione supportata, e le release pi\u00f9 vecchie (sotto Node 18) non ricevono pi\u00f9 aggiornamenti di sicurezza.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node --version\n# Output atteso: v20.x.x oppure v22.x.x\n\nnpm --version\n# Output atteso: 10.x.x<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se non hai una toolchain C disponibile (capita spesso su immagini Docker minimali o su alcune piattaforme serverless), usa <code>bcryptjs<\/code> al posto di <code>bcrypt<\/code>. L&#8217;API \u00e8 quasi identica e tutto il codice di questo tutorial funziona cambiando solo la riga di import. Ne parliamo nello step 11.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"anatomia-di-un-hash-bcrypt-come-leggere-la-stringa-2b\">Anatomia di un hash bcrypt: come leggere la stringa $2b$<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Un hash bcrypt \u00e8 una stringa autocontenuta di 60 caratteri. Tutto ci\u00f2 che serve per verificare una password (algoritmo, costo, salt e digest) \u00e8 impacchettato in quella stringa. Capirne la struttura ti aiuta a fare debug e a non commettere errori di archiviazione nel database.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$2b$12$R9h\/cIPz0gi.URNNX3kh2OPST9\/PgBkqquzi.Ss7KIUgO2t0jWMUW\n |  |  |                     |\n |  |  |                     +-- digest (31 caratteri, l'hash vero e proprio)\n |  |  +------------------------ salt (22 caratteri, generato casualmente)\n |  +--------------------------- cost factor (qui 12, cioe 2^12 iterazioni)\n +------------------------------ identificatore di versione ($2b$)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La tabella riassume i quattro segmenti. Quando li conosci, capisci a colpo d&#8217;occhio con quale costo \u00e8 stato generato un hash e se \u00e8 il momento di ricalcolarlo.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Segmento<\/th><th>Esempio<\/th><th>Significato<\/th><\/tr><\/thead><tbody><tr><td>Prefisso versione<\/td><td>$2b$<\/td><td>Variante moderna e sicura dell&#8217;algoritmo<\/td><\/tr><tr><td>Cost factor<\/td><td>12<\/td><td>Numero di round, lavoro pari a 2^12 = 4096 iterazioni<\/td><\/tr><tr><td>Salt<\/td><td>22 caratteri base64<\/td><td>Valore casuale, diverso per ogni hash<\/td><\/tr><tr><td>Digest<\/td><td>31 caratteri base64<\/td><td>Risultato del calcolo Blowfish<\/td><\/tr><tr><td>Lunghezza totale<\/td><td>60 caratteri<\/td><td>Dimensiona cos\u00ec la colonna del database<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Errore frequente: dimensionare la colonna del database a 32 o 40 caratteri pensando a un hash &#8220;tipico&#8221;. Un hash bcrypt occupa sempre 60 caratteri. Usa <code>VARCHAR(60)<\/code> come minimo, oppure <code>VARCHAR(72)<\/code> per avere margine in caso di prefissi futuri. Una colonna troppo corta tronca l&#8217;hash in silenzio e rende impossibile ogni login successivo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"bcrypt-vs-argon2-vs-scrypt-quale-algoritmo-scegliere\">bcrypt vs Argon2 vs scrypt: quale algoritmo scegliere<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bcrypt non \u00e8 l&#8217;unica opzione. Le tre alternative serie nel 2026 sono bcrypt, Argon2 e scrypt. La differenza fondamentale \u00e8 il tipo di risorsa che rendono costosa: bcrypt \u00e8 CPU-hard (consuma cicli di processore), mentre Argon2 e scrypt sono memory-hard (consumano grandi quantit\u00e0 di RAM). La durezza in memoria \u00e8 pi\u00f9 efficace contro gli attacchi con hardware specializzato come gli ASIC, perch\u00e9 la memoria veloce \u00e8 costosa da scalare.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Caratteristica<\/th><th>bcrypt<\/th><th>Argon2id<\/th><th>scrypt<\/th><\/tr><\/thead><tbody><tr><td>Tipo di durezza<\/td><td>CPU<\/td><td>Memoria + CPU<\/td><td>Memoria<\/td><\/tr><tr><td>Anno di nascita<\/td><td>1999<\/td><td>2015 (vincitore PHC)<\/td><td>2009<\/td><\/tr><tr><td>Supporto in Node.js<\/td><td>Pacchetto bcrypt<\/td><td>Pacchetto argon2<\/td><td>Nativo (crypto.scrypt)<\/td><\/tr><tr><td>Parametri da regolare<\/td><td>1 (cost)<\/td><td>3 (memoria, iterazioni, parallelismo)<\/td><td>3 (N, r, p)<\/td><\/tr><tr><td>Limite input<\/td><td>72 byte<\/td><td>Nessun limite pratico<\/td><td>Nessun limite pratico<\/td><\/tr><tr><td>Raccomandazione OWASP<\/td><td>Accettato, cost &gt;= 10<\/td><td>Preferito (19 MiB, 2 iter, 1 par)<\/td><td>Accettato<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">La guida OWASP sullo storage delle password preferisce Argon2id, ma bcrypt resta una scelta pienamente accettata e, per molti progetti, la pi\u00f9 pragmatica. \u00c8 maturo, ha un solo parametro da regolare, \u00e8 disponibile ovunque ed \u00e8 praticamente impossibile da configurare male. Se stai partendo da zero e vuoi il massimo, valuta Argon2id. Se vuoi qualcosa di robusto, semplice e a prova di errore, bcrypt \u00e8 la risposta giusta. Per un confronto sui fondamenti, leggi anche la nostra guida sulle <a href=\"\/it\/funzioni-hash\/\">funzioni hash crittografiche<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-3-creare-il-progetto-e-installare-bcrypt\">Step 1-3: Creare il progetto e installare bcrypt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Iniziamo dalle fondamenta. Creiamo una cartella di progetto, inizializziamo npm e installiamo le dipendenze. Tutto il tutorial costruisce un&#8217;unica applicazione coerente, quindi i comandi che seguono sono il punto di partenza.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-1-inizializzare-la-cartella-di-progetto\">Step 1: Inizializzare la cartella di progetto<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir bcrypt-auth-demo\ncd bcrypt-auth-demo\nnpm init -y\n\n# Output:\n# Wrote to \/percorso\/bcrypt-auth-demo\/package.json<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-installare-bcrypt-ed-express\">Step 2: Installare bcrypt ed Express<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install bcrypt express\n\n# Output atteso (le versioni esatte possono variare):\n# added 57 packages, and audited 58 packages in 4s\n# found 0 vulnerabilities<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se l&#8217;installazione di bcrypt fallisce con un errore di compilazione (node-gyp), salta direttamente allo step 11 dove usiamo <code>bcryptjs<\/code>, che non richiede alcuna compilazione. Per ora, procediamo con il pacchetto nativo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-abilitare-i-moduli-es-e-la-struttura-base\">Step 3: Abilitare i moduli ES e la struttura base<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Apri <code>package.json<\/code> e aggiungi <code>\"type\": \"module\"<\/code> per usare la sintassi import moderna. Poi crea un file <code>hash.js<\/code> vuoto. La struttura del progetto \u00e8 minimale e cresce a ogni step.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ package.json (estratto)\n{\n  \"name\": \"bcrypt-auth-demo\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"bcrypt\": \"^6.0.0\",\n    \"express\": \"^4.21.0\"\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-6-hashing-verifica-e-scelta-del-cost-factor\">Step 4-6: Hashing, verifica e scelta del cost factor<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ora il cuore del tutorial: trasformare una password in un hash bcrypt e verificarla. Sono tre operazioni distinte, ognuna con la sua funzione asincrona dedicata.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-4-generare-un-hash-con-bcrypt\">Step 4: Generare un hash con bcrypt<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Scrivi questo codice in <code>hash.js<\/code>. La funzione <code>bcrypt.hash<\/code> prende la password in chiaro e il cost factor, genera automaticamente un salt casuale e restituisce la stringa di 60 caratteri. Usa sempre la versione asincrona basata su promise: la versione sincrona (<code>hashSync<\/code>) blocca l&#8217;event loop e va evitata in un server.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import bcrypt from 'bcrypt';\n\nconst COST = 12;\n\nasync function creaHash(password) {\n  const hash = await bcrypt.hash(password, COST);\n  return hash;\n}\n\nconst hash = await creaHash('passwordSuperSegreta!');\nconsole.log(hash);\n\/\/ Output: $2b$12$R9h\/cIPz0gi.URNNX3kh2OPST9\/PgBk...\n\/\/ La tua stringa sara diversa: il salt e casuale.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Esegui con <code>node hash.js<\/code>. Nota che eseguendo il file due volte ottieni due hash diversi, anche con la stessa password. \u00c8 il salt al lavoro: questa \u00e8 la propriet\u00e0 che neutralizza le rainbow table.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-5-verificare-una-password-con-bcrypt-compare\">Step 5: Verificare una password con bcrypt.compare<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Per verificare un login non si decifra l&#8217;hash (\u00e8 impossibile, \u00e8 una funzione a senso unico). Si passa la password fornita e l&#8217;hash memorizzato a <code>bcrypt.compare<\/code>, che estrae salt e costo dall&#8217;hash, ricalcola e confronta. La funzione restituisce un booleano.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>async function verifica(password, hashMemorizzato) {\n  const corretta = await bcrypt.compare(password, hashMemorizzato);\n  return corretta;\n}\n\nconsole.log(await verifica('passwordSuperSegreta!', hash));\n\/\/ Output: true\n\nconsole.log(await verifica('passwordSbagliata', hash));\n\/\/ Output: false<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Un dettaglio cruciale di sicurezza: <code>bcrypt.compare<\/code> esegue un confronto a tempo costante. Non interrompe il calcolo al primo byte diverso, quindi non rivela informazioni tramite il tempo di risposta. Non sostituire mai questa funzione con un confronto manuale di stringhe (<code>===<\/code>): apriresti la porta agli attacchi di timing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-6-misurare-e-scegliere-il-cost-factor-giusto\">Step 6: Misurare e scegliere il cost factor giusto<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Il cost factor \u00e8 la decisione di sicurezza pi\u00f9 importante. Troppo basso, e l&#8217;hash \u00e8 debole. Troppo alto, e ogni login rallenta e il server diventa vulnerabile ad attacchi di tipo denial of service via login ripetuti. L&#8217;obiettivo pratico \u00e8 che un singolo hash richieda tra 250 e 500 millisecondi sull&#8217;hardware di produzione. Misura sempre sul tuo server reale.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import bcrypt from 'bcrypt';\n\nfor (const cost of [10, 11, 12, 13, 14]) {\n  const inizio = process.hrtime.bigint();\n  await bcrypt.hash('benchmark', cost);\n  const fine = process.hrtime.bigint();\n  const ms = Number(fine - inizio) \/ 1_000_000;\n  console.log(`cost ${cost}: ${ms.toFixed(0)} ms`);\n}\n\n\/\/ Esempio di output su un core a 2 GHz:\n\/\/ cost 10: 65 ms\n\/\/ cost 11: 130 ms\n\/\/ cost 12: 260 ms\n\/\/ cost 13: 520 ms\n\/\/ cost 14: 1040 ms<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nota il raddoppio a ogni incremento: \u00e8 la natura esponenziale di 2 elevato al cost. La guida OWASP fissa il minimo a 10. Nel 2026, per i login interattivi, il valore predefinito ragionevole \u00e8 12, da alzare a 13 o 14 su hardware potente. La tabella seguente riassume i compromessi.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Cost factor<\/th><th>Iterazioni (2^cost)<\/th><th>Tempo indicativo<\/th><th>Uso consigliato<\/th><\/tr><\/thead><tbody><tr><td>10<\/td><td>1.024<\/td><td>~65 ms<\/td><td>Minimo OWASP assoluto<\/td><\/tr><tr><td>11<\/td><td>2.048<\/td><td>~130 ms<\/td><td>Hardware modesto<\/td><\/tr><tr><td>12<\/td><td>4.096<\/td><td>~260 ms<\/td><td>Default consigliato 2026<\/td><\/tr><tr><td>13<\/td><td>8.192<\/td><td>~520 ms<\/td><td>Server moderni<\/td><\/tr><tr><td>14<\/td><td>16.384<\/td><td>~1040 ms<\/td><td>Massima sicurezza, dati sensibili<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-7-9-costruire-lapi-di-autenticazione-con-express\">Step 7-9: Costruire l&#8217;API di autenticazione con Express<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Mettiamo insieme i pezzi in una API reale. Costruiamo due endpoint: registrazione (<code>\/register<\/code>) e login (<code>\/login<\/code>). Per semplicit\u00e0 usiamo un archivio in memoria, ma il flusso \u00e8 identico con un database vero come PostgreSQL o MongoDB.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-7-lendpoint-di-registrazione\">Step 7: L&#8217;endpoint di registrazione<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Crea un file <code>server.js<\/code>. L&#8217;endpoint di registrazione riceve email e password, genera l&#8217;hash con bcrypt e lo memorizza. La password in chiaro non tocca mai il database e non viene mai loggata.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import express from 'express';\nimport bcrypt from 'bcrypt';\n\nconst app = express();\napp.use(express.json());\n\nconst COST = 12;\nconst utenti = new Map(); \/\/ sostituisci con un database reale\n\napp.post('\/register', async (req, res) =&gt; {\n  const { email, password } = req.body;\n\n  if (!email || !password) {\n    return res.status(400).json({ errore: 'Email e password obbligatorie' });\n  }\n  if (utenti.has(email)) {\n    return res.status(409).json({ errore: 'Utente gia registrato' });\n  }\n\n  const hash = await bcrypt.hash(password, COST);\n  utenti.set(email, { email, hash });\n\n  res.status(201).json({ messaggio: 'Registrazione completata' });\n});<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-8-lendpoint-di-login\">Step 8: L&#8217;endpoint di login<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Il login recupera l&#8217;utente, confronta la password con <code>bcrypt.compare<\/code> e risponde. Nota un dettaglio di sicurezza importante: il messaggio di errore \u00e8 identico sia che l&#8217;utente non esista, sia che la password sia sbagliata. Rivelare quale dei due \u00e8 fallito aiuterebbe un attaccante a enumerare gli account validi.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app.post('\/login', async (req, res) =&gt; {\n  const { email, password } = req.body;\n  const utente = utenti.get(email);\n\n  \/\/ Messaggio generico: niente enumerazione degli account\n  const erroreGenerico = { errore: 'Credenziali non valide' };\n\n  if (!utente) {\n    \/\/ Esegui comunque un confronto fittizio per non rivelare nulla via timing\n    await bcrypt.compare(password, '$2b$12$invalidinvalidinvalidinvalidinvalidinvalidinv');\n    return res.status(401).json(erroreGenerico);\n  }\n\n  const corretta = await bcrypt.compare(password, utente.hash);\n  if (!corretta) {\n    return res.status(401).json(erroreGenerico);\n  }\n\n  res.json({ messaggio: 'Login riuscito', email });\n});\n\napp.listen(3000, () =&gt; console.log('Server in ascolto sulla porta 3000'));<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-9-testare-lapi-con-curl\">Step 9: Testare l&#8217;API con curl<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Avvia il server con <code>node server.js<\/code> e prova gli endpoint da un altro terminale. Ecco il flusso completo registrazione e login con l&#8217;output atteso.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -X POST http:\/\/localhost:3000\/register \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"email\":\"mario@example.com\",\"password\":\"Tornado-Viola-92!\"}'\n# Output: {\"messaggio\":\"Registrazione completata\"}\n\ncurl -X POST http:\/\/localhost:3000\/login \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"email\":\"mario@example.com\",\"password\":\"Tornado-Viola-92!\"}'\n# Output: {\"messaggio\":\"Login riuscito\",\"email\":\"mario@example.com\"}\n\ncurl -X POST http:\/\/localhost:3000\/login \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"email\":\"mario@example.com\",\"password\":\"sbagliata\"}'\n# Output: {\"errore\":\"Credenziali non valide\"}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Hai un&#8217;API di autenticazione funzionante. Per completarla con i token di sessione, leggi la nostra guida sull&#8217;<a href=\"\/it\/autenticazione-jwt-nodejs\/\">autenticazione JWT in Node.js<\/a>, che si combina perfettamente con il flusso di login appena costruito.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-10-ricalcolare-gli-hash-al-login-rehash-adattivo\">Step 10: Ricalcolare gli hash al login (rehash adattivo)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Qui sfruttiamo la vera superpotenza di bcrypt: l&#8217;adattivit\u00e0. Quando alzi il cost factor predefinito (per esempio da 12 a 13), gli hash gi\u00e0 nel database restano al vecchio costo. Non puoi ricalcolarli senza la password in chiaro, ma c&#8217;\u00e8 un momento perfetto in cui ce l&#8217;hai: il login. La strategia \u00e8 verificare la password, e se l&#8217;hash usa un costo inferiore a quello attuale, rigenerarlo in modo trasparente.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import bcrypt from 'bcrypt';\n\nconst COST_ATTUALE = 13;\n\nasync function loginConRehash(password, utente) {\n  const corretta = await bcrypt.compare(password, utente.hash);\n  if (!corretta) return false;\n\n  \/\/ Estrai il costo dall'hash memorizzato\n  const costoHash = bcrypt.getRounds(utente.hash);\n\n  if (costoHash &lt; COST_ATTUALE) {\n    \/\/ La password e corretta e l'abbiamo in chiaro: aggiorniamo l'hash\n    utente.hash = await bcrypt.hash(password, COST_ATTUALE);\n    \/\/ salva utente.hash nel database\n    console.log(`Hash aggiornato da costo ${costoHash} a ${COST_ATTUALE}`);\n  }\n  return true;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La funzione <code>bcrypt.getRounds<\/code> legge il cost factor direttamente dalla stringa dell&#8217;hash. Con questo schema, il tuo intero database migra gradualmente al nuovo costo man mano che gli utenti accedono, senza interruzioni e senza dover mai chiedere a nessuno di reimpostare la password. \u00c8 il pattern che separa un&#8217;implementazione amatoriale da una professionale.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-11-il-limite-dei-72-byte-e-il-pre-hashing\">Step 11: Il limite dei 72 byte e il pre-hashing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bcrypt ha un limite poco conosciuto ma critico: ignora tutto ci\u00f2 che supera i 72 byte di input. Attenzione, sono 72 byte, non 72 caratteri. Un carattere accentato o un&#8217;emoji in UTF-8 pu\u00f2 occupare fino a 4 byte, quindi una passphrase lunga pu\u00f2 raggiungere il limite prima di quanto pensi. Tutto ci\u00f2 che eccede viene troncato in silenzio, il che significa che due password diverse ma con gli stessi primi 72 byte risultano equivalenti.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La soluzione raccomandata dall&#8217;OWASP \u00e8 il pre-hashing: prima di passare la password a bcrypt, calcola un digest SHA-256 e codificalo in base64. Lo SHA-256 produce sempre 32 byte, e la sua codifica base64 sta comodamente sotto i 72 byte, eliminando il problema del troncamento qualunque sia la lunghezza originale della password.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import bcrypt from 'bcrypt';\nimport { createHash } from 'node:crypto';\n\nconst COST = 12;\n\nfunction preHash(password) {\n  \/\/ SHA-256 -&gt; 32 byte, poi base64 -&gt; ~44 caratteri, sempre sotto i 72 byte\n  return createHash('sha256').update(password).digest('base64');\n}\n\nasync function hashSicuro(password) {\n  return bcrypt.hash(preHash(password), COST);\n}\n\nasync function verificaSicura(password, hash) {\n  return bcrypt.compare(preHash(password), hash);\n}\n\n\/\/ Funziona anche con password lunghissime\nconst lunga = 'a'.repeat(200) + ' caffe ristretto doppio';\nconst h = await hashSicuro(lunga);\nconsole.log(await verificaSicura(lunga, h)); \/\/ true<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Una sola avvertenza: se adotti il pre-hashing, devi applicarlo in modo coerente sia in fase di hashing sia in fase di verifica. E se hai gi\u00e0 un database di hash creati senza pre-hashing, non puoi cambiare strategia retroattivamente. In quel caso, migra al nuovo schema al login, con lo stesso pattern di rehash dello step 10.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"usare-bcryptjs-negli-ambienti-serverless\">Usare bcryptjs negli ambienti serverless<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Se non puoi compilare moduli nativi (AWS Lambda con runtime limitati, edge function, alcuni container), sostituisci <code>bcrypt<\/code> con <code>bcryptjs<\/code>, l&#8217;implementazione in pura JavaScript con zero dipendenze. \u00c8 pi\u00f9 lenta del binding nativo, ma a livello di codice cambia solo l&#8217;import: tutte le funzioni (<code>hash<\/code>, <code>compare<\/code>, <code>getRounds<\/code>) hanno la stessa firma.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install bcryptjs\n\n# Nel codice, cambia solo questa riga:\n# import bcrypt from 'bcrypt';\nimport bcrypt from 'bcryptjs';\n# Tutto il resto del tutorial funziona identico.<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-12-validazione-delle-password-e-blocklist-nist-2026\">Step 12: Validazione delle password e blocklist (NIST 2026)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Un hashing impeccabile non serve a nulla se l&#8217;utente sceglie &#8220;password123&#8221;. L&#8217;ultimo step aggiunge una validazione conforme alle linee guida NIST SP 800-63B (confermate nella revisione 800-63-4 del 2024). Le regole moderne ribaltano i vecchi dogmi.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il NIST richiede di accettare password lunghe almeno fino a 64 caratteri e di imporre un minimo di almeno 8 caratteri. Soprattutto, il NIST sconsiglia le regole di composizione obbligatorie (la classica imposizione di maiuscole, numeri e simboli) perch\u00e9 spingono gli utenti verso schemi prevedibili. La regola davvero efficace \u00e8 un&#8217;altra: controllare la password contro una blocklist di password compromesse, trapelate in violazioni passate o troppo comuni.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const BLOCKLIST = new Set([\n  'password', '123456', '12345678', 'qwerty',\n  'password123', 'admin', 'iloveyou', 'letmein'\n  \/\/ in produzione: carica una lista reale di password trapelate\n]);\n\nfunction validaPassword(password) {\n  if (typeof password !== 'string') return 'Password non valida';\n  if (password.length &lt; 8) return 'Minimo 8 caratteri';\n  if (password.length &gt; 64) return 'Massimo 64 caratteri';\n  if (BLOCKLIST.has(password.toLowerCase())) {\n    return 'Questa password e troppo comune o compromessa';\n  }\n  return null; \/\/ null significa: password valida\n}\n\nconst errore = validaPassword('password');\nconsole.log(errore); \/\/ \"Questa password e troppo comune o compromessa\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In produzione, sostituisci la blocklist di esempio con un controllo serio. Il servizio &#8220;Pwned Passwords&#8221; di Have I Been Pwned espone una API basata su k-anonymity che permette di verificare se una password \u00e8 apparsa in una violazione senza mai inviarla per intero. Combinata con bcrypt, questa validazione copre la maggior parte dei rischi reali sugli account.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"5-errori-comuni-da-evitare-con-bcrypt\">5 errori comuni da evitare con bcrypt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La maggior parte delle vulnerabilit\u00e0 non nasce da un difetto di bcrypt, ma da un uso sbagliato. Ecco i cinque errori che vediamo pi\u00f9 spesso nelle code review.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Cost factor troppo basso o lasciato al default di esempio.<\/strong> Un cost di 4 o 6 trovato in un tutorial vecchio rende l&#8217;hash quasi inutile. Nel 2026 usa almeno 12 e misura sul tuo hardware.<\/li><li><strong>Usare le versioni sincrone in un server.<\/strong> <code>bcrypt.hashSync<\/code> e <code>compareSync<\/code> bloccano l&#8217;event loop di Node.js. Con un cost di 12, ogni chiamata congela il server per centinaia di millisecondi. Usa sempre le versioni asincrone con await.<\/li><li><strong>Confrontare gli hash a mano.<\/strong> Non usare mai <code>password === hash<\/code> o un confronto di stringhe. Solo <code>bcrypt.compare<\/code> garantisce il confronto a tempo costante contro gli attacchi di timing.<\/li><li><strong>Ignorare il limite dei 72 byte.<\/strong> Senza pre-hashing, le passphrase lunghe vengono troncate in silenzio e perdono entropia. Applica lo SHA-256 pi\u00f9 base64 dello step 11.<\/li><li><strong>Dimensionare male la colonna del database.<\/strong> Un hash bcrypt \u00e8 sempre di 60 caratteri. Una colonna <code>VARCHAR(40)<\/code> tronca l&#8217;hash e rompe ogni login. Usa almeno <code>VARCHAR(60)<\/code>.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"risoluzione-dei-problemi-8-errori-frequenti\">Risoluzione dei problemi: 8 errori frequenti<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quando qualcosa non funziona, la tabella seguente copre i sintomi pi\u00f9 diffusi durante l&#8217;integrazione di bcrypt, con la causa e la soluzione concreta.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Sintomo<\/th><th>Causa probabile<\/th><th>Soluzione<\/th><\/tr><\/thead><tbody><tr><td>node-gyp rebuild failed durante npm install<\/td><td>Manca la toolchain C\/Python<\/td><td>Installa build-essential e Python 3, oppure passa a bcryptjs<\/td><\/tr><tr><td>compare restituisce sempre false<\/td><td>Hash troncato dal database<\/td><td>Verifica che la colonna sia almeno VARCHAR(60)<\/td><\/tr><tr><td>Error: data and hash arguments required<\/td><td>Uno dei due valori \u00e8 undefined<\/td><td>Controlla che req.body sia parsato (express.json)<\/td><\/tr><tr><td>Login lentissimo, server bloccato<\/td><td>Uso di hashSync\/compareSync<\/td><td>Passa alle versioni asincrone con await<\/td><\/tr><tr><td>Password lunghe accettate ma login fallisce<\/td><td>Troncamento a 72 byte non gestito<\/td><td>Applica il pre-hashing SHA-256 pi\u00f9 base64<\/td><\/tr><tr><td>Invalid salt version \/ hash non valido<\/td><td>Hash corrotto o generato da altra libreria<\/td><td>Rigenera l&#8217;hash, verifica il prefisso $2b$<\/td><\/tr><tr><td>Tempi di hashing diversi tra dev e prod<\/td><td>Hardware diverso, stesso cost<\/td><td>Misura e ritara il cost in produzione<\/td><\/tr><tr><td>Module did not self-register<\/td><td>Binary nativo non compatibile con la versione di Node<\/td><td>Esegui npm rebuild bcrypt dopo un upgrade di Node<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"tecniche-avanzate-e-best-practice-per-la-produzione\">Tecniche avanzate e best practice per la produzione<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Una volta che le basi funzionano, alcune accortezze portano l&#8217;implementazione a livello produzione. Sono dettagli che fanno la differenza durante un audit di sicurezza.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Configura il cost via variabile d&#8217;ambiente.<\/strong> Tieni il cost factor in <code>process.env.BCRYPT_COST<\/code> cos\u00ec lo aggiorni senza ridistribuire il codice. Questo abilita anche il rehash adattivo dello step 10.<\/li><li><strong>Aggiungi un rate limiting sul login.<\/strong> bcrypt protegge gli hash rubati, non gli attacchi online. Limita i tentativi di login per IP e per account (per esempio 5 tentativi al minuto) per fermare il brute force diretto.<\/li><li><strong>Considera un &#8220;pepper&#8221; applicativo.<\/strong> Oltre al salt (memorizzato nell&#8217;hash), un pepper \u00e8 un segreto globale conservato fuori dal database (in un secret manager o HSM). Se il database trapela ma il pepper no, gli hash restano fuori portata. Si applica tipicamente con un HMAC prima di bcrypt.<\/li><li><strong>Non loggare mai le password.<\/strong> Sembra ovvio, ma un middleware di logging troppo verboso o un messaggio di errore pu\u00f2 finire per registrare il body della richiesta. Filtra esplicitamente i campi sensibili.<\/li><li><strong>Forza HTTPS ovunque.<\/strong> bcrypt protegge la password a riposo, ma in transito la difende solo TLS. Approfondisci nella nostra guida su <a href=\"\/it\/https-e-tls\/\">HTTPS e TLS<\/a>.<\/li><li><strong>Pianifica la rotazione del cost.<\/strong> Rivedi il cost factor una volta all&#8217;anno. Se l&#8217;hardware \u00e8 migliorato, alza il valore e lascia che il rehash al login faccia il resto.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Per i sistemi che gestiscono dati particolarmente sensibili, valuta di affiancare a bcrypt una strategia di difesa in profondit\u00e0: autenticazione a pi\u00f9 fattori, monitoraggio dei login anomali e cifratura del database a riposo. L&#8217;hashing delle password \u00e8 un pilastro, non l&#8217;intero edificio. Per il quadro completo sulla protezione delle credenziali, leggi la nostra guida sulla <a href=\"\/it\/sicurezza-delle-password\/\">sicurezza delle password<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"il-progetto-completo-tutto-il-codice-insieme\">Il progetto completo: tutto il codice insieme<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ecco il file <code>server.js<\/code> finale che unisce hashing sicuro, pre-hashing, validazione NIST, rehash adattivo e i due endpoint. \u00c8 un punto di partenza pronto da adattare al tuo database.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import express from 'express';\nimport bcrypt from 'bcrypt';\nimport { createHash } from 'node:crypto';\n\nconst app = express();\napp.use(express.json());\n\nconst COST = Number(process.env.BCRYPT_COST) || 12;\nconst utenti = new Map();\nconst BLOCKLIST = new Set(['password', '123456', 'qwerty', 'admin']);\n\nconst preHash = (p) =&gt; createHash('sha256').update(p).digest('base64');\n\nfunction validaPassword(p) {\n  if (typeof p !== 'string' || p.length &lt; 8) return 'Minimo 8 caratteri';\n  if (p.length &gt; 64) return 'Massimo 64 caratteri';\n  if (BLOCKLIST.has(p.toLowerCase())) return 'Password troppo comune';\n  return null;\n}\n\napp.post('\/register', async (req, res) =&gt; {\n  const { email, password } = req.body;\n  const erroreVal = validaPassword(password);\n  if (!email || erroreVal) {\n    return res.status(400).json({ errore: erroreVal || 'Email obbligatoria' });\n  }\n  if (utenti.has(email)) {\n    return res.status(409).json({ errore: 'Utente gia registrato' });\n  }\n  const hash = await bcrypt.hash(preHash(password), COST);\n  utenti.set(email, { email, hash });\n  res.status(201).json({ messaggio: 'Registrazione completata' });\n});\n\napp.post('\/login', async (req, res) =&gt; {\n  const { email, password } = req.body;\n  const utente = utenti.get(email);\n  const errore = { errore: 'Credenziali non valide' };\n\n  if (!utente) {\n    await bcrypt.compare('x', '$2b$12$invalidinvalidinvalidinvalidinvalidinvalidinv');\n    return res.status(401).json(errore);\n  }\n  const corretta = await bcrypt.compare(preHash(password), utente.hash);\n  if (!corretta) return res.status(401).json(errore);\n\n  if (bcrypt.getRounds(utente.hash) &lt; COST) {\n    utente.hash = await bcrypt.hash(preHash(password), COST);\n  }\n  res.json({ messaggio: 'Login riuscito', email });\n});\n\napp.listen(3000, () =&gt; console.log('Server pronto sulla porta 3000'));<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Meno di 50 righe per un sistema di autenticazione che rispetta le linee guida OWASP e NIST del 2026. Questa \u00e8 la forza di bcrypt: la sicurezza corretta \u00e8 anche la pi\u00f9 semplice da scrivere.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"perche-lhashing-conta-la-lezione-delle-violazioni\">Perch\u00e9 l&#8217;hashing conta: la lezione delle violazioni<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La teoria diventa concreta quando guardi cosa succede dopo una violazione. Quando un database trapela, il destino delle password dipende interamente da come erano archiviate. Con il testo in chiaro, ogni account \u00e8 compromesso immediatamente. Con MD5 o SHA-1 non salati, i tool di cracking offline svuotano la lista in poche ore. Con bcrypt e un cost adeguato, anche un attaccante con risorse importanti riesce a recuperare solo una piccola frazione delle password pi\u00f9 deboli, e nei tempi lunghi.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Questo \u00e8 il vero valore di bcrypt: trasforma una violazione catastrofica in un incidente contenibile. D\u00e0 a te e ai tuoi utenti il tempo di reagire, cambiare le credenziali e limitare i danni. Per capire come avvengono le fughe di dati e come ridurre la superficie d&#8217;attacco, leggi il nostro approfondimento sulle <a href=\"\/it\/violazioni-di-dati\/\">violazioni di dati<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Come riassume la guida OWASP sullo storage delle password, l&#8217;obiettivo non \u00e8 rendere l&#8217;hashing impossibile da invertire (\u00e8 gi\u00e0 garantito dalla matematica), ma renderlo cos\u00ec costoso da scoraggiare l&#8217;attacco su larga scala. Il cost factor di bcrypt \u00e8 esattamente la leva che regola quel costo, ed \u00e8 interamente nelle tue mani.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"domande-frequenti-su-bcrypt-in-node-js\">Domande frequenti su bcrypt in Node.js<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"qual-e-il-cost-factor-ideale-per-bcrypt-nel-2026\">Qual \u00e8 il cost factor ideale per bcrypt nel 2026?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Il valore predefinito ragionevole \u00e8 12, che corrisponde a circa 250 millisecondi per hash su un core moderno. La guida OWASP fissa il minimo a 10. Su server potenti puoi salire a 13 o 14. La regola pratica: misura sul tuo hardware di produzione e scegli il costo che mantiene il tempo di hashing tra 250 e 500 millisecondi.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"bcrypt-o-argon2-quale-dovrei-usare\">bcrypt o Argon2: quale dovrei usare?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;OWASP preferisce Argon2id perch\u00e9 \u00e8 memory-hard, quindi pi\u00f9 resistente agli attacchi con hardware specializzato. Tuttavia bcrypt resta pienamente accettato ed \u00e8 pi\u00f9 semplice da configurare correttamente, con un solo parametro. Per un nuovo progetto che vuole il massimo, scegli Argon2id. Per robustezza e semplicit\u00e0 a prova di errore, bcrypt \u00e8 un&#8217;ottima scelta.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"devo-memorizzare-il-salt-separatamente-dallhash-bcrypt\">Devo memorizzare il salt separatamente dall&#8217;hash bcrypt?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. bcrypt incorpora il salt direttamente nella stringa dell&#8217;hash di 60 caratteri. Ti basta salvare quell&#8217;unica stringa nel database. La funzione <code>compare<\/code> estrae automaticamente il salt e il cost factor quando verifica una password. Non serve alcuna colonna separata per il salt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"perche-bcrypt-tronca-le-password-a-72-byte\">Perch\u00e9 bcrypt tronca le password a 72 byte?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c8 una conseguenza del design basato su Blowfish, che usa al massimo 72 byte di input per il setup della chiave. Tutto ci\u00f2 che eccede viene ignorato. La soluzione raccomandata \u00e8 il pre-hashing con SHA-256 e codifica base64 prima di passare la password a bcrypt, come mostrato nello step 11. Cos\u00ec nessuna parte della password viene mai persa.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"bcrypt-o-bcryptjs-ce-differenza-di-sicurezza\">bcrypt o bcryptjs: c&#8217;\u00e8 differenza di sicurezza?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Nessuna differenza a livello di sicurezza dell&#8217;algoritmo: entrambi implementano lo stesso bcrypt e producono hash compatibili. La differenza \u00e8 prestazionale. Il pacchetto <code>bcrypt<\/code> usa un binding C++ nativo ed \u00e8 pi\u00f9 veloce. <code>bcryptjs<\/code> \u00e8 pura JavaScript, pi\u00f9 lento, ma non richiede compilazione, ideale per ambienti serverless o edge dove non puoi compilare moduli nativi.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"come-aggiorno-gli-hash-quando-aumento-il-cost-factor\">Come aggiorno gli hash quando aumento il cost factor?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Non puoi ricalcolare un hash senza la password in chiaro, ma la ottieni al momento del login. Lo schema \u00e8: verifica la password, leggi il costo dell&#8217;hash memorizzato con <code>bcrypt.getRounds<\/code>, e se \u00e8 inferiore al costo attuale rigenera l&#8217;hash con il nuovo valore e salvalo. Il database migra gradualmente senza che gli utenti debbano fare nulla.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"e-sicuro-usare-bcrypt-hashsync-in-produzione\">\u00c8 sicuro usare bcrypt.hashSync in produzione?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No, evitalo. Le versioni sincrone bloccano l&#8217;event loop di Node.js per tutta la durata del calcolo, centinaia di millisecondi con un cost di 12. Sotto carico, il server diventa irraggiungibile. Usa sempre le versioni asincrone basate su promise con <code>await<\/code>, che non bloccano l&#8217;elaborazione delle altre richieste.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"bcrypt-protegge-dagli-attacchi-di-brute-force-online\">bcrypt protegge dagli attacchi di brute force online?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Solo in parte. bcrypt rende costoso il cracking offline degli hash rubati, ma non ferma chi prova migliaia di password contro il tuo endpoint di login in tempo reale. Per quello servono difese aggiuntive: rate limiting per IP e per account, blocco temporaneo dopo tentativi falliti e, idealmente, autenticazione a pi\u00f9 fattori.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"risorse-e-approfondimenti\">Risorse e approfondimenti<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Documentazione e standard ufficiali usati in questo tutorial: la <a href=\"https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Password_Storage_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP Password Storage Cheat Sheet<\/a>, le linee guida <a href=\"https:\/\/pages.nist.gov\/800-63-3\/sp800-63b.html\" target=\"_blank\" rel=\"noopener\">NIST SP 800-63B<\/a>, il repository ufficiale <a href=\"https:\/\/github.com\/kelektiv\/node.bcrypt.js\" target=\"_blank\" rel=\"noopener\">node.bcrypt.js su GitHub<\/a>, la voce <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bcrypt\" target=\"_blank\" rel=\"noopener\">bcrypt su Wikipedia<\/a> e la documentazione del <a href=\"https:\/\/nodejs.org\/api\/crypto.html\" target=\"_blank\" rel=\"noopener\">modulo crypto di Node.js<\/a> per l&#8217;alternativa scrypt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"approfondimenti-correlati\">Approfondimenti correlati<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"\/it\/autenticazione-jwt-nodejs\/\">Autenticazione JWT in Node.js: 12 Step [2026]<\/a><\/li><li><a href=\"\/it\/crittografia-end-to-end-nodejs\/\">Crittografia End-to-End in Node.js: 12 Step [2026]<\/a><\/li><li><a href=\"\/it\/sicurezza-delle-password\/\">Sicurezza delle password: lunghezza, hashing e secondo fattore<\/a><\/li><li><a href=\"\/it\/funzioni-hash\/\">Funzioni hash crittografiche: propriet\u00e0, differenze e usi<\/a><\/li><li><a href=\"\/it\/firme-digitali\/\">Firme digitali: come funzionano e perch\u00e9 le collisioni le minacciano<\/a><\/li><li><a href=\"\/it\/cryptography-hub\/\">Crittografia: funzioni hash, SHA e fiducia digitale<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Ogni volta che un database di credenziali finisce online, la differenza tra un incidente gestibile e un disastro dipende da una scelta tecnica fatta mesi prima: come sono state archiviate\u2026<\/p>\n","protected":false},"author":7,"featured_media":83,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-82","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cryptography"],"_links":{"self":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/82","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/comments?post=82"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/82\/revisions"}],"predecessor-version":[{"id":84,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/82\/revisions\/84"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/media\/83"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/media?parent=82"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/categories?post=82"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/tags?post=82"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}