Skip to content
Torna al blog
Sicurezza

bcrypt vs Argon2 vs scrypt: hashing password nel 2026

Confronta bcrypt, Argon2id e scrypt con i parametri OWASP 2026, framework decisionale ed esempi di codice. Scegli oggi l'hash giusto per la tua app web.

18 min di lettura

bcrypt vs Argon2 vs scrypt: hashing password nel 2026

Risposta breve: per qualsiasi nuovo progetto nel 2026, usa Argon2id con m=19456, t=2, p=1. È la baseline dell’OWASP Password Storage Cheat Sheet, e oggi è l’algoritmo di hashing password con la migliore resistenza a GPU e canali laterali che puoi mettere in produzione.

Se Argon2 non è disponibile nel tuo stack (raro, ma capita su alcuni runtime embedded o datati), scegli scrypt con N=2^17, r=8, p=1. Usa bcrypt con cost=12 solo quando sei vincolato a un sistema legacy che già parla bcrypt e non puoi introdurre nuove dipendenze. Resta su PBKDF2-HMAC-SHA-256 con 600.000 iterazioni quando la conformità FIPS-140 è obbligatoria.

AlgoritmoParametri OWASP 2026Quando sceglierlo
Argon2idm=19456 KiB, t=2, p=1Default per i nuovi progetti
scryptN=2^17, r=8, p=1Argon2 non disponibile
bcryptcost=12 (min 10)Solo sistemi legacy
PBKDF2HMAC-SHA-256, 600k iterazioniFIPS-140 richiesto

Il resto dell’articolo spiega da dove vengono questi numeri, come tararli sul tuo hardware e come migrare senza forzare un reset password. Devi generare password di test robuste per i benchmark? Usa il Generatore di Password Casuali. Per il quadro complessivo, leggi la guida alla Sicurezza Web online.

Perché l’hashing delle password è diverso dall’hashing generico

Da fuori le funzioni hash si assomigliano tutte: dati in ingresso, digest a lunghezza fissa in uscita, e non si torna indietro. Ma gli obiettivi di progetto per “hash di una ISO da 4 GB” e “hash di una password di 12 caratteri” sono esattamente opposti. Uno deve andare veloce quanto il silicio permette. L’altro deve andare lento quanto il tuo budget di latenza al login tollera.

Confonderli è il modo in cui un data breach diventa un account takeover.

Perché MD5 e SHA-256 non bastano per le password

Gli hash di uso generale come MD5, SHA-1 e SHA-256 sono nati per il throughput. Macinano gigabyte al secondo su CPU comuni e decine di gigabyte al secondo su GPU. Perfetti per checksum di file e content addressing, disastrosi per le password.

I benchmark di Hashcat su una sola RTX 4090 mostrano circa 164 GH/s per MD5 e 22 GH/s per SHA-256 nel 2024. Una password di otto caratteri alfanumerici minuscoli (36^8 ≈ 2,8 × 10^12 candidati) cade a una singola GPU in meno di un minuto contro MD5 e in un paio di minuti contro SHA-256. Un database compromesso che memorizza sha256(password) è di fatto in chiaro.

Nemmeno il salt ti salva. Il salt impedisce alle rainbow table precalcolate di funzionare, ma non rallenta affatto un attacco mirato al singolo account: l’attaccante hasha ogni candidato concatenato al salt trafugato.

Per i checksum non legati alla sicurezza, MD5 e SHA-256 restano utili: è esattamente il caso d’uso di strumenti come il Generatore Hash MD5 e SHA-256. Per un confronto approfondito su quando ciascun algoritmo è appropriato, leggi MD5 vs SHA-256. Per le password, invece, serve un hash deliberatamente lento.

Le tre proprietà di un hash password moderno

Un hash password che valga la pena spedire nel 2026 ha tre proprietà:

  1. Lento per design, con un work factor regolabile. Il login dovrebbe richiedere 100–500 ms: abbastanza veloce da non infastidire l’utente, abbastanza lento da bruciare giorni a un attaccante offline per ogni milione di tentativi. Il work factor deve essere un parametro, così puoi alzarlo man mano che l’hardware migliora.
  2. Salt per record. Un salt casuale unico per ogni password sconfigge le rainbow table e costringe l’attaccante ad attaccare ogni account separatamente. Gli algoritmi moderni generano e incorporano il salt nella stringa di output al posto tuo.
  3. Memory-hard. Le GPU e gli ASIC sono veloci nel calcolo ma costosi sulla memoria ad alta banda. Un algoritmo che richiede decine di MiB per hash costringe l’attaccante a procurarsi RAM proporzionale al suo parallelismo, distruggendo la convenienza economica delle farm di GPU.

bcrypt centra (1) e (2) ma non (3). scrypt è stato il primo algoritmo a coprire tutti e tre i punti. Argon2 ha raffinato il design e ha vinto la Password Hashing Competition. La sezione successiva li analizza uno per uno.

I tre algoritmi: architettura e tradeoff

bcrypt: basato su Blowfish, time-hard

bcrypt è stato progettato nel 1999 da Niels Provos e David Mazières per OpenBSD. Si basa sul cifrario Blowfish, con una fase costosa di key-setup (“EksBlowfish”) ripetuta 2^cost volte. L’unico parametro regolabile è il cost factor (anche detto “log rounds”): a ogni incremento il lavoro raddoppia. Un hash con cost=10 esegue 1.024 key schedule; cost=14 ne esegue 16.384.

Un hash bcrypt si presenta così:

$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
 │  │  │                      │
 │  │  │                      └─ hash base64 di 31 caratteri
 │  │  └─ salt base64 di 22 caratteri
 │  └─ cost factor (12)
 └─ identificatore algoritmo ($2b$ = bcrypt v2)

Il formato è auto-descrittivo: verify() legge cost e salt dalla stringa memorizzata, senza bisogno di colonne separate.

Gli svantaggi sono concreti. L’impronta in memoria di bcrypt è di circa 4 KiB, abbastanza piccola da permettere a una GPU di fascia alta di far girare migliaia di core bcrypt in parallelo. C’è poi un altro punto delicato: bcrypt tronca silenziosamente l’input a 72 byte. Una passphrase di 100 caratteri ha la stessa sicurezza dei suoi primi 72 byte. Il cost massimo è 31, ma già oltre ~16 la latenza al login inizia a soffrire su hardware comune.

scrypt: il pioniere memory-hard

scrypt è stato pubblicato nel 2009 da Colin Percival per il servizio di backup Tarsnap e standardizzato come RFC 7914 nel 2016. Ha introdotto il concetto di memory-hardness: l’algoritmo riempie un grande buffer con dati pseudo-casuali, poi legge da posizioni casuali, costringendo qualsiasi implementazione ad allocare davvero la memoria.

scrypt prende tre parametri:

  • N: costo CPU/memoria (deve essere una potenza di 2)
  • r: block size in byte (moltiplicatore su memoria e round di mixing)
  • p: parallelismo (calcoli indipendenti, usato soprattutto per scalare il tempo CPU senza scalare la memoria)

L’uso di memoria è approssimativamente 128 × N × r byte. Con i valori raccomandati da OWASP N=2^17, r=8, fa 128 × 131072 × 8 = 134.217.728 byte, cioè 128 MiB per hash.

scrypt è anche una funzione di derivazione di chiave, non solo un hash password. Lo trovi nei wallet di criptovaluta, nella cifratura full-disk e nella proof-of-work originale di Litecoin. Questo doppio ruolo è comodo quando ti servono memorizzazione password e derivazione di chiave nella stessa libreria.

Argon2 (id/i/d): vincitore della Password Hashing Competition

La Password Hashing Competition è andata avanti dal 2013 al 2015, valutando 24 algoritmi candidati su memory-hardness, resistenza ai canali laterali e semplicità implementativa. Ha vinto Argon2. È stato standardizzato come RFC 9106 nel 2021.

Argon2 ha tre varianti. Le differenze stanno in come la memoria viene indirizzata durante il mixing:

  • Argon2d usa indirizzi dipendenti dai dati. Massimizza la resistenza ad attacchi GPU e ASIC ma fa trapelare informazioni attraverso canali laterali di cache-timing. Adatto alla proof-of-work delle criptovalute, non all’autenticazione.
  • Argon2i usa indirizzi indipendenti dai dati. Sicuro contro i canali laterali, ma leggermente più debole contro gli attacchi GPU di tipo tradeoff.
  • Argon2id è ibrido: la prima metà del primo passaggio usa l’indicizzazione di Argon2i (sicura contro i canali laterali) e il resto usa l’indicizzazione di Argon2d (resistente alle GPU). RFC 9106 raccomanda esplicitamente Argon2id per l’hashing password, e così fa anche OWASP.

Argon2 prende tre parametri:

  • m: memoria in KiB
  • t: time cost (numero di passaggi sul buffer di memoria)
  • p: parallelismo (numero di lane elaborate in concorrenza)

Un hash Argon2id usa il formato di stringa PHC e si presenta così:

$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG

Come per bcrypt, tutti i parametri sono incorporati nella stringa, quindi verify() non ha bisogno di una tabella di parametri separata.

Parametri raccomandati OWASP 2026

L’OWASP Password Storage Cheat Sheet è il riferimento canonico. I numeri qui sotto rispecchiano la sua guida attuale. Sono conservativi, dimensionati per un tipico web server con un budget di latenza al login di 100–500 ms, e dovresti comunque fare benchmark sul tuo hardware prima di andare in produzione.

Parametri Argon2id: prima scelta

Raccomandazione baseline OWASP: m=19456 (19 MiB), t=2, p=1.

Se il tuo server ha più RAM disponibile, puoi spostare il lavoro tra memoria e tempo. RFC 9106 pubblica profili equivalenti; OWASP raccomanda uno qualsiasi di questi:

memoryCost (m)timeCost (t)parallelism (p)RAM per hash
471041146 MiB
194562119 MiB (baseline)
122883112 MiB
9216419 MiB
7168517 MiB

Regola pratica per la taratura. Scegli prima m in base al budget di RAM per i login concorrenti di picco. Se ti aspetti 100 login simultanei e hai 4 GiB liberi, sono 40 MiB per hash. Poi aumenta t finché un singolo verify non impiega 100–500 ms sulla CPU di produzione. Lascia p=1 a meno che tu non abbia un motivo specifico multi-core per cambiarlo (la maggior parte dei framework web assegna già un thread per richiesta).

Parametri scrypt: quando Argon2 non è disponibile

Raccomandazione OWASP: N=2^17 (131072), r=8, p=1, che usa 128 MiB per hash.

Se 128 MiB per login concorrente sono troppi per il tuo server, OWASP ammette profili più deboli:

NrpRAM per hash
2^1781128 MiB (preferito)
2^168164 MiB
2^158132 MiB

N deve essere una potenza di due. Aumentare r aumenta proporzionalmente sia memoria sia lavoro CPU; aumentare p aumenta il lavoro CPU senza aumentare la memoria per istanza. Per l’hashing password, lascia r e p ai default e tara solo N.

bcrypt: cost factor 10+ solo per legacy

OWASP non raccomanda più bcrypt per i nuovi progetti, ma è ancora ovunque: Devise, Spring Security, ASP.NET Identity e innumerevoli sistemi di auth fatti in casa lo usano per default.

Se sei vincolato a bcrypt, le regole sono:

  • Cost factor minimo per bcrypt: 10. Sotto 10 è abbastanza veloce da permettere a una sola GPU di finire un database trafugato in pochi giorni.
  • Raccomandato: da 12 a 14, a seconda dell’hardware. Su un server x86 moderno, cost=12 impiega circa 250 ms per hash; cost=13 circa 500 ms.
  • Punta a 100–300 ms per verify sull’hardware di produzione. Misura, non tirare a indovinare.
  • Ricorda il limite di input di 72 byte. Se gli utenti possono scegliere passphrase, fai un pre-hash con SHA-256 (vedi la FAQ).

La resistenza alle GPU di bcrypt è limitata dalla sua impronta di memoria di 4 KiB. Nessun cost factor di bcrypt eguaglierà mai la memory-hardness di Argon2id; quando puoi, scegli Argon2id.

Un riferimento pratico: su un server EPYC del 2024, bcrypt(cost=12) gira in circa 250 ms; su un laptop di fascia alta, più vicino ai 350 ms. Se i tuoi numeri si discostano da 100–500 ms di un ordine di grandezza, ricontrolla che la tua libreria stia davvero usando bcrypt nativo e non un polyfill JavaScript lento (alcuni bundler eliminano le dipendenze native nelle build serverless).

PBKDF2: la via per la conformità FIPS-140

PBKDF2 (RFC 8018) è l’algoritmo da scegliere come ultima spiaggia in materia di sicurezza. È più vecchio di bcrypt, non è memory-hard e cede agli attacchi GPU più in fretta di tutti e tre quelli di sopra. Però è l’unica primitiva di hashing password validata FIPS-140, cosa che conta per il governo federale, la conformità HIPAA in sanità e certi deployment finanziari.

Quando ti serve PBKDF2, usa:

  • HMAC-SHA-256 come PRF (non usare SHA-1; non usare SHA-256 senza HMAC)
  • 600.000 iterazioni minimo (baseline OWASP 2026)
  • Almeno un salt casuale di 16 byte per password

Se FIPS non ti riguarda, preferisci Argon2id. Il design a output fisso e memoria fissa di PBKDF2 fa sì che ogni dollaro di silicio GPU che un attaccante compra si traduca direttamente in più tentativi al secondo.

L’SP 800-63B del NIST definisce PBKDF2-HMAC “approvato” per l’hashing password ma si ferma prima di raccomandarlo rispetto alle alternative memory-hard. Leggi così: il NIST permette PBKDF2 perché ritirarlo invaliderebbe ogni deployment governativo legacy, non perché sia la scelta migliore per un progetto greenfield.

Framework decisionale: quale algoritmo scegliere?

Tabella di confronto

DimensionebcryptscryptArgon2idPBKDF2
Memory-hardNoNo
Resistenza GPUMediaAltaMolto altaBassa
Resistenza canali lateraliMediaMediaAlta (id)Media
Complessità parametri1 (cost)3 (N, r, p)3 (m, t, p)1 (iterazioni)
Maturità delle librerieEccellenteBuonaBuonaEccellente
Limite lunghezza input72 byteNessunoNessunoNessuno
Standardizzazionede factoRFC 7914RFC 9106RFC 8018
Stato OWASP 2026Solo legacyAlternativaPrima sceltaSolo FIPS

Usa Argon2id per default

Per un nuovo progetto (tipica web app, stack moderno Node/Python/Go/Rust/JVM, nessun vincolo FIPS) usa Argon2id con m=19456, t=2, p=1. Ottieni la migliore resistenza disponibile a GPU e canali laterali, un formato a parametri incorporati che sopravvive agli upgrade di libreria, e nessuna sorpresa sul limite di lunghezza dell’input. L’ecosistema di librerie è maturo: argon2 su npm, argon2-cffi su PyPI, golang.org/x/crypto/argon2, il crate argon2 su crates.io, tutti mantenuti e con benchmark.

Quando scegliere scrypt o bcrypt

Scegli scrypt quando Argon2 non è disponibile nel tuo runtime (davvero raro nel 2026: ce l’hanno persino Cloudflare Workers e Deno) o quando hai già un sistema in produzione basato su scrypt e il costo della migrazione supera il delta di sicurezza. scrypt resta un algoritmo solido; gli manca solo la rifinitura sui canali laterali di Argon2id.

Scegli bcrypt quando stai mantenendo un sistema legacy, hai un requisito stringente di minimizzazione delle dipendenze (niente codice nativo, nessun pacchetto extra) e il limite di 72 byte sull’input è accettabile per la tua utenza. bcrypt è in produzione su scala internet da due decenni; i suoi failure mode sono noti.

Scegli PBKDF2 quando lo dice il regolatore. È l’unica ragione. Se il tuo auditor accetta Argon2id (un numero crescente lo fa per workload non-FIPS), usa Argon2id.

Errori comuni da evitare

La maggior parte dei breach di memorizzazione password dell’ultimo decennio risale a un piccolo insieme di errori ingegneristici ricorrenti. Nessuno di essi è esotico; tutti vengono intercettati rivedendo il codice di autenticazione con questa lista davanti.

  • Hashare le password con SHA-256 o MD5 grezzi. È il più grande errore nella memorizzazione password. Vedi MD5 vs SHA-256 per capire perché sono inadatti alle password.
  • Riutilizzare un unico salt globale per tutti gli utenti. Il salt deve essere unico per record. Argon2 e bcrypt te ne generano uno; non sovrascriverlo.
  • Impostare un tempo di hash sotto i 50 ms. Hai barattato sicurezza per un guadagno di velocità che nessun utente può percepire. Punta a 100–500 ms.
  • Impostare un tempo di hash sopra 1 secondo. Hai creato un vettore di denial-of-service contro il tuo stesso endpoint di login. Tieni il tetto attorno ai 500 ms.
  • Hashare le password lato client e mandare il digest al server. L’hash è diventato la password. Chi ruba il database può autenticarsi senza neanche invertirlo. Hasha sempre sul server.
  • Memorizzare i parametri dell’algoritmo in una colonna separata. Il formato di stringa PHC li incorpora nell’hash. Usalo.
  • Loggare password o hash nella gestione degli errori. Entrambi appartengono all’utente, non al tuo log aggregator. Sanitizzali al livello di parsing della richiesta prima che raggiungano qualsiasi logger.
  • Trattare le eccezioni di verify() come fallimenti di autenticazione. Una libreria che lancia eccezione su un hash memorizzato malformato deve far emergere l’errore, non scivolare in “password sbagliata”. Distingui tra “password sbagliata” (ritorna 401) e “hash memorizzato corrotto” (ritorna 500 e svegli l’on-call).

Implementazione reale

Argon2id in Node.js

Il pacchetto argon2 (binding nativi all’implementazione di riferimento) è la scelta canonica su Node:

import argon2 from 'argon2';

// Hashing al signup o al cambio password
const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 19456,  // 19 MiB
  timeCost: 2,
  parallelism: 1,
});
// → '$argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash>'

// Verifica al login
const ok = await argon2.verify(hash, candidate);
if (!ok) throw new Error('Invalid credentials');

// Rileva parametri obsoleti e ri-hasha al login andato a buon fine
if (argon2.needsRehash(hash, { type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1 })) {
  const upgraded = await argon2.hash(candidate, {
    type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1,
  });
  await db.users.update({ id: user.id }, { password_hash: upgraded });
}

Il passo needsRehash è il segreto della migrazione di lungo periodo: ogni login andato a buon fine diventa l’occasione per portare l’hash memorizzato ai parametri attuali, senza disturbare l’utente.

Lo stesso schema in Python con argon2-cffi:

from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError

ph = PasswordHasher(memory_cost=19456, time_cost=2, parallelism=1)

# Hash
stored = ph.hash(password)

# Verifica
try:
    ph.verify(stored, candidate)
except VerifyMismatchError:
    raise ValueError('Invalid credentials')

# Ri-hash al cambio di parametri
if ph.check_needs_rehash(stored):
    stored = ph.hash(candidate)

In Go con golang.org/x/crypto/argon2:

import (
    "crypto/rand"
    "golang.org/x/crypto/argon2"
)

func hashPassword(password string) ([]byte, []byte) {
    salt := make([]byte, 16)
    rand.Read(salt)
    hash := argon2.IDKey([]byte(password), salt, 2, 19456, 1, 32)
    return hash, salt
}

La libreria standard di Go non include un encoder in formato PHC; se usi direttamente la primitiva argon2.IDKey, sei tu a doverti occupare di codificare parametri e salt insieme all’hash. La maggior parte dei progetti Go usa un wrapper come github.com/alexedwards/argon2id per questo.

Rust con il crate argon2 è altrettanto idiomatico:

use argon2::{Argon2, PasswordHasher, PasswordVerifier, password_hash::{SaltString, rand_core::OsRng}};

let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();  // Argon2id, m=19456, t=2, p=1 di default
let hash = argon2.hash_password(password.as_bytes(), &salt)?.to_string();

// Alla verifica
let parsed = argon2::password_hash::PasswordHash::new(&hash)?;
argon2.verify_password(candidate.as_bytes(), &parsed)?;

In tutti e tre i runtime, la stringa prodotta è interscambiabile: un hash creato in Node viene verificato senza problemi in Python o Rust. Questa compatibilità cross-runtime rende Argon2 una scommessa più sicura per le architetture poliglotte rispetto ai wrapper legati a un singolo algoritmo.

Pattern di migrazione da bcrypt a Argon2id

Quasi mai capita di poter ripulire la tabella utenti e ricominciare. Il giusto pattern di migrazione è lo stesso usato nella sezione MD5-to-bcrypt della FAQ del nostro generatore di hash: un upgrade graduale, guidato dal login.

Aggiungi una colonna che traccia l’algoritmo:

ALTER TABLE users ADD COLUMN password_algo VARCHAR(16) NOT NULL DEFAULT 'bcrypt';

Al login, smista verso il verifier giusto:

async function verifyAndMaybeRehash(user, candidate) {
  let ok;
  if (user.password_algo === 'argon2id') {
    ok = await argon2.verify(user.password_hash, candidate);
  } else if (user.password_algo === 'bcrypt') {
    ok = await bcrypt.compare(candidate, user.password_hash);
    if (ok) {
      // Verifica legacy ok → ri-hash con Argon2id
      const newHash = await argon2.hash(candidate, {
        type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1,
      });
      await db.users.update({ id: user.id }, {
        password_hash: newHash,
        password_algo: 'argon2id',
      });
    }
  }
  return ok;
}

Imposta una finestra di dismissione di 6–12 mesi. Manda un’email del tipo “la tua password è memorizzata con un metodo obsoleto, accedi per aggiornarla” al nono mese. Dopo 12 mesi, gli account ancora su bcrypt chiedono un reset password forzato al login successivo. Gli utenti attivi migrano in modo trasparente; gli account inattivi subiscono un singolo evento di attrito.

Lo stesso schema vale per migrare da scrypt o PBKDF2. L’unico stato che ti serve è la colonna password_algo.

Pepper, limiti di lunghezza e insidie di codifica

Alcuni spigoli vivi che mordono nelle distribuzioni reali:

Pepper. Un pepper è un segreto applicativo aggiunto a ogni password prima dell’hashing, memorizzato separatamente dal database (in un KMS, una env var o Hashicorp Vault). Se il database trapela ma il segreto applicativo no, gli hash trafugati restano inattaccabili senza il pepper. Applicalo come HMAC, non per concatenazione:

import { createHmac } from 'crypto';
const peppered = createHmac('sha256', process.env.PEPPER).update(password).digest();
const hash = await argon2.hash(peppered, { type: argon2.argon2id, /* ... */ });

Ruota il pepper di rado (richiede un re-hash) ma supporta la rotazione versionandolo: PEPPER_V2, con fallback a PEPPER_V1 in fase di verifica.

Limite di 72 byte di bcrypt. Se devi usare bcrypt e vuoi supportare password di lunghezza arbitraria, fai un pre-hash con SHA-256 e codifica in base64 (per evitare i NUL byte, che bcrypt gestisce in modo incoerente):

import { createHash } from 'crypto';
const prepped = createHash('sha256').update(password, 'utf8').digest('base64');
const hash = await bcrypt.hash(prepped, 12);

La stessa trasformazione prepped deve girare in fase di verifica. Documentala nel codice di auth con un commento gigante; il te futuro ringrazierà il te attuale.

Normalizzazione UTF-8. La stringa "café" può essere codificata come c-a-f-é (4 codepoint, NFC) o come c-a-f-e + acuto combinante (5 codepoint, NFD). Sembrano identiche ma producono hash diversi. Normalizza sempre in NFC prima di hashare:

const normalized = password.normalize('NFC');

Questo capita con tastiere mobili e copia-incolla da PDF più spesso di quanto immagineresti.

Mai pre-hashare lato client. Un hash calcolato dal client e mandato al server è la nuova password. Chi legge il database può autenticarsi. Hasha sul server, punto. I JWT non cambiano questa regola: vedi Come decodificare un token JWT per capire cosa i JWT autenticano e cosa no.

Misura sull’hardware di produzione, non sul tuo laptop. Un laptop Intel di 13ª gen che esegue Argon2id con m=19456, t=2, p=1 finisce in circa 35 ms. Gli stessi parametri su un’istanza EC2 t3.small arrivano vicino ai 180 ms; su un Raspberry Pi 4, oltre 600 ms. Scegli l’hardware che girerà davvero in produzione, cronometra 1.000 verify e tara dalla mediana. Vale la pena misurare anche la varianza di latenza al login dei container serverless a freddo: i cold start su Lambda possono aggiungere 200–800 ms che nulla hanno a che fare con l’hashing.

FAQ

Qual è la differenza tra hashing password e cifratura?

L’hashing è a senso unico: calcoli un’impronta a lunghezza fissa che non si può invertire per recuperare l’input. La cifratura è bidirezionale: con la chiave giusta, puoi decifrare e tornare all’originale. Le password vanno hashate, non cifrate. Un server non deve poter recuperare la password di nessun utente, così una fuga del database non si trasforma in fuga di credenziali.

Perché non posso usare semplicemente SHA-256 per le password?

SHA-256 è progettato per la velocità. Una GPU moderna calcola 22 miliardi di hash SHA-256 al secondo, il che significa che una password di 8 caratteri minuscoli da un database trafugato cade in pochi minuti. Gli hash password hanno bisogno di tre proprietà che a SHA-256 mancano: esecuzione deliberatamente lenta, salt per record e memory-hardness. Il principio di tradeoff è lo stesso spiegato nella linea guida “Don’t Use MD5 for Security” del nostro Generatore Hash MD5 e SHA-256; puoi approfondire come gli attaccanti trasformano hash deboli in chiaro nell’articolo Entropia delle password.

bcrypt è ancora sicuro nel 2026?

bcrypt in sé non è stato compromesso. Il key schedule basato su Blowfish resta crittograficamente solido. Quel che è cambiato è il modello di minaccia: GPU e ASIC fanno della mancanza di memory-hardness di bcrypt una debolezza significativa rispetto ad Argon2id. La posizione OWASP 2026 è che bcrypt è accettabile per i sistemi legacy con cost ≥ 10, ma i nuovi progetti dovrebbero scegliere Argon2id.

Argon2i vs Argon2d vs Argon2id: quale dovrei usare?

Usa Argon2id. RFC 9106 lo specifica come variante raccomandata per l’hashing password. Argon2i è data-independent (sicuro contro i canali laterali ma più debole contro gli attacchi GPU di tipo tradeoff). Argon2d è data-dependent (forte contro le GPU ma vulnerabile ai canali laterali di cache-timing). Argon2id è un ibrido che ottiene entrambe le proprietà in un colpo solo.

Come scelgo i parametri Argon2id per la mia app?

Parti dalla baseline OWASP: m=19456, t=2, p=1. Poi misura sulla CPU di produzione e regola:

  1. Decidi il budget di RAM per login (es. 50 MiB al picco di concorrenza).
  2. Imposta m a quel valore o sotto.
  3. Lancia argon2.hash() in un loop e misura il tempo reale.
  4. Aumenta t finché la mediana non rientra tra 100 e 500 ms.

Lascia p=1 a meno che tu non abbia profilato e sappia che il parallelismo multi-lane aiuta il tuo runtime. Per server di autenticazione ad alto traffico, sbilanciarsi verso un t più alto e un m più basso spesso dà più margine sulla RAM.

Cos’è il limite di 72 byte di bcrypt e come gestisco le passphrase lunghe?

bcrypt manda il suo input al key schedule di Blowfish, che tronca a 72 byte. Una passphrase di 150 caratteri ha la stessa sicurezza dei suoi primi 72 byte: il resto è ignorato. La soluzione è fare un pre-hash con SHA-256 (32 byte) o SHA-512 (64 byte), codificare il digest in base64 per evitare NUL byte, e darlo in pasto a bcrypt. Argon2id e scrypt non hanno questo limite; accettano direttamente input di lunghezza arbitraria.

Posso migrare da bcrypt ad Argon2 senza forzare reset password?

Sì. Lo schema è: memorizza entrambi gli algoritmi dietro una colonna password_algo, smista la verifica verso la libreria giusta e, a ogni verify bcrypt andato a buon fine, ri-hasha subito con Argon2id e aggiorna la riga. Gli utenti attivi migrano in silenzio entro la loro normale cadenza di login. Imposta una finestra di dismissione di 6–12 mesi per gli account inattivi, poi forza un reset password per ogni record ancora su bcrypt. Lo stesso schema vale per qualsiasi migrazione da algoritmo ad algoritmo.

PBKDF2 è ancora una buona scelta nel 2026?

Solo quando la conformità FIPS-140 ti costringe: tipico nel governo federale, nella sanità regolata (HIPAA) e in certi sistemi finanziari. Usa HMAC-SHA-256 come PRF con almeno 600.000 iterazioni. PBKDF2 non è memory-hard, quindi cede agli attacchi GPU più in fretta di Argon2id a parità di budget di latenza. Se FIPS non si applica, scegli Argon2id e salta la ginnastica di compliance.


La risposta sull’hashing password nel 2026 è breve: default su Argon2id con i parametri baseline OWASP, fallback su scrypt se Argon2 non è disponibile, bcrypt solo dove il legacy lo impone, e PBKDF2 riservato ai sistemi vincolati FIPS. Affianca all’hash un salt per record (ogni libreria moderna lo fa in automatico), un pepper applicativo memorizzato fuori dal database, e un loop di re-hash innescato dal login che ti permette di alzare il work factor man mano che l’hardware migliora.

Genera un set rappresentativo di password con il Generatore di Password Casuali, fai benchmark del percorso di verify sulla tua CPU di produzione e scrivi i parametri in un file di costanti, così il prossimo ingegnere saprà esattamente cosa alzare nel 2028. Il contesto di sicurezza completo (TLS, gestione delle sessioni, rate limiting, MFA) vive nella nostra guida alla Sicurezza Web online. Scegli oggi l’hash giusto per la tua app.

Articoli correlati

Vedi tutti gli articoli