Il 19 agosto 2026 Have I Been Pwned ha aggiunto alla sua lista OzHairAndBeauty, una violazione da quasi due milioni di record. Il giorno prima era toccato a RingCentral. Il database del progetto di Troy Hunt conta oggi 1.029 violazioni catalogate, di cui 77 aggiunte solo nel 2026, e continua a crescere quasi ogni giorno. Se lavori nell’IT, gestisci utenti o semplicemente vuoi sapere se una tua password gira già nelle mani sbagliate, questa guida ti porta passo dopo passo dall’uso base del sito fino a un sistema automatico di monitoraggio via API, con codice pronto in Python e Node.js.

Perché nel 2026 controllare le violazioni dei dati è ormai indispensabile

I numeri raccontano una tendenza precisa. Secondo il DLA Piper GDPR Data Breach Survey, tra il 28 gennaio 2025 e il 27 gennaio 2026 il numero medio di notifiche di violazione al giorno in Europa è salito da 363 a 443, un aumento del 22%. Non è un caso isolato: sommando i conteggi pubblici di tutte le violazioni presenti oggi in Have I Been Pwned si arriva a circa 17,8 miliardi di record esposti (con inevitabili sovrapposizioni tra un leak e l’altro, perché lo stesso indirizzo email spesso compare in più violazioni diverse).

La violazione più grande mai caricata sul servizio è SynthientCredentialStuffingThreatData: un archivio da 1.957.476.021 credenziali, datato 11 aprile 2025 e aggiunto al catalogo il 6 novembre 2025, frutto dell’aggregazione di liste usate per attacchi di credential stuffing. Non è un breach di un singolo servizio ma la fotografia di quanto materiale rubato circoli già oggi tra i database dei criminali informatici.

Per un’azienda italiana o europea, ignorare questo scenario significa esporsi a due rischi concreti: account takeover su servizi interni ed esterni, e mancato rispetto degli obblighi di notifica entro 72 ore previsti dal GDPR. Sapere in anticipo se le tue email o quelle dei tuoi utenti compaiono in un leak ti dà il tempo di reagire prima che qualcuno lo faccia al posto tuo. Trovi il contesto più ampio sulle violazioni di dati nella nostra guida su come avvengono e come proteggersi.

Le violazioni più grandi mai raccolte da Have I Been Pwned

Per capire la scala del problema, ecco la classifica reale delle dieci violazioni più grandi presenti oggi nel catalogo, ordinate per numero di record esposti (dati estratti dall’endpoint pubblico dell’API il 20 agosto 2026). Alcune sono breach di un singolo servizio, altre sono compilation che aggregano credenziali rubate da fonti diverse nel tempo.

ViolazioneRecord espostiData del breach
SynthientCredentialStuffingThreatData1.957.476.02111 aprile 2025
Collection1772.904.9917 gennaio 2019
VerificationsIO763.117.24125 febbraio 2019
OnlinerSpambot711.477.62228 agosto 2017
PDL (People Data Labs)622.161.05216 ottobre 2019
ExploitIn593.427.11913 ottobre 2016
Facebook509.458.5281 agosto 2019
AntiPublic457.962.53816 dicembre 2016
RiverCityMedia393.430.3091 gennaio 2017
TelegramCombolists361.468.09928 maggio 2024

Nota come le prime posizioni siano occupate soprattutto da compilation (“combolist”) piuttosto che da singoli breach aziendali: sono raccolte di credenziali già rubate in passato, riorganizzate e ricombinate dai criminali per rendere più efficaci gli attacchi di credential stuffing. È proprio questo tipo di materiale, aggiornato con cadenza quasi settimanale, a rendere il controllo periodico più utile di una verifica fatta una tantum.

Cos’è Have I Been Pwned e chi c’è dietro il progetto

Have I Been Pwned (HIBP) è un servizio gratuito creato nel 2013 dal ricercatore di sicurezza australiano Troy Hunt. L’idea originaria era semplice: raccogliere i dump di violazioni pubblicate online (o vendute nei forum underground) e permettere a chiunque di verificare se il proprio indirizzo email compare in una di esse. Da allora il progetto è cresciuto fino a diventare uno standard de facto per la verifica delle violazioni, usato anche da browser, gestori di password e dipartimenti di sicurezza aziendale in tutto il mondo.

Oggi il catalogo conta 1.029 violazioni, di cui 987 marcate come “verificate” (cioè confermate come autentiche dal team di HIBP) e 16 classificate come semplici spam list. Il servizio offre due componenti distinte: la ricerca per violazione, che indica in quali breach compare un indirizzo email o un dominio, e Pwned Passwords, un elenco di password compromesse che puoi interrogare senza mai inviare la password in chiaro. Secondo i dati pubblicati sul sito ufficiale, il solo servizio di controllo password gestisce oltre 18 miliardi di richieste al mese, distribuite attraverso la rete Cloudflare.

La ricerca per email e dominio richiede oggi una sottoscrizione a pagamento con chiave API (dal 2019, per limitare l’abuso del servizio da parte di chi voleva usarlo per scopi malevoli), mentre Pwned Passwords resta completamente gratuito e senza autenticazione. Questa distinzione è il punto di partenza per capire come costruire il tuo sistema di controllo.

Un dettaglio che spesso sorprende: la lista completa delle violazioni resta consultabile pubblicamente e senza chiave, tramite l’endpoint /api/v3/breaches. È da lì che abbiamo estratto i dati di questa guida, incluso il conteggio aggiornato di 1.029 violazioni. Solo la ricerca mirata per un indirizzo email o un dominio specifico richiede l’autenticazione, perché è quella parte del servizio più facilmente sfruttabile per verificare in massa credenziali rubate.

Prerequisiti: cosa ti serve prima di iniziare

Prima di iniziare, prepara questo elenco. Non serve installare software complesso: la maggior parte degli strumenti sono già presenti su un sistema di sviluppo standard.

  • curl versione 8.x o superiore (per i test rapidi da terminale)
  • Python 3.12 o successivo, con il modulo requests installato (pip install requests)
  • Node.js 20 LTS o successivo, con npm per l’esempio di integrazione Express
  • Un account email valido per ricevere eventuali notifiche di test
  • Una chiave API HIBP v3, necessaria solo dal Passo 4 in poi (a pagamento, si parte da 4,39 $/mese)
  • Un webhook Slack o un account SMTP, se vuoi seguire anche il passo sulle notifiche automatiche
  • Accesso a un server o a un servizio di cron (anche una semplice VPS Linux) per pianificare i controlli periodici

Nota bene: i primi tre passi di questa guida non richiedono alcuna chiave API e sono completamente gratuiti. Se ti serve solo verificare qualche password o account personale, puoi fermarti lì.

Passo 1: la verifica rapida sul sito ufficiale

Il modo più veloce per iniziare è visitare haveibeenpwned.com e digitare il tuo indirizzo email nel campo di ricerca. In pochi secondi il sito ti mostra se compari in una o più delle 1.029 violazioni catalogate, con nome del servizio colpito, data del breach e tipologia di dati esposti (password, numeri di telefono, indirizzi, ecc.).

Questo controllo manuale è perfetto per un uso occasionale, ma ha un limite evidente: non scala. Se devi verificare decine o centinaia di account aziendali, o vuoi ripetere il controllo ogni settimana, hai bisogno di automazione. È qui che entra in gioco l’API, che vedremo a partire dal Passo 3.

Un altro dettaglio utile della verifica sul sito: puoi filtrare i risultati per tipo di dato esposto (email, password, numeri di telefono, dati di pagamento) direttamente dall’interfaccia, senza scrivere una riga di codice. È un modo rapido per capire, a colpo d’occhio, se una violazione specifica riguarda solo l’indirizzo email o anche informazioni più sensibili che richiedono un intervento più urgente.

Passo 2: capire il modello k-anonymity di Pwned Passwords

Prima di scrivere codice, vale la pena capire come funziona Pwned Passwords, perché il meccanismo è elegante e risolve un problema di privacy non banale: come faccio a sapere se la mia password è stata rubata senza doverla inviare a un server di terzi?

La risposta è il k-anonymity. Il client calcola in locale l’hash SHA-1 della password, poi invia al server solo i primi 5 caratteri di quell’hash (il “prefisso”). Il server risponde con l’elenco completo di tutti i suffissi di hash che iniziano con quel prefisso, insieme al numero di volte in cui ciascuno è comparso nei leak raccolti. Il confronto finale, cioè la verifica che il tuo hash completo sia nella lista, avviene sul tuo dispositivo. La password vera e propria, e persino l’hash completo, non lasciano mai il tuo computer.

Puoi verificarlo tu stesso. Prendi la password “password” (sì, ancora usata da milioni di persone), calcola lo SHA-1 (5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD), invia solo il prefisso 5BAA6 e cerca il suffisso corrispondente nella risposta:

curl -s "https://api.pwnedpasswords.com/range/5BAA6" | grep "1E4C9B93F3F0682250B6CF8331B7EE68FD"

# Output reale (verificato il 20 agosto 2026):
# 1E4C9B93F3F0682250B6CF8331B7EE68FD8:52372427

52.372.427 comparse per una singola password. È il motivo per cui il controllo delle password compromesse dovrebbe far parte di ogni flusso di registrazione, non solo un consiglio da dare “una tantum” agli utenti.

Passo 3: controllare una password compromessa via API con curl e Python

Ora automatizziamo il controllo con uno script. In Python bastano poche righe: calcoliamo lo SHA-1 della password, spezziamo l’hash in prefisso e suffisso, interroghiamo l’endpoint range e cerchiamo il suffisso nella risposta.

import hashlib
import requests

def check_password(password: str) -> int:
    sha1 = hashlib.sha1(password.encode("utf-8")).hexdigest().upper()
    prefix, suffix = sha1[:5], sha1[5:]

    resp = requests.get(
        f"https://api.pwnedpasswords.com/range/{prefix}",
        headers={"Add-Padding": "true"},
        timeout=10,
    )
    resp.raise_for_status()

    for line in resp.text.splitlines():
        hash_suffix, count = line.split(":")
        if hash_suffix == suffix:
            return int(count)
    return 0

count = check_password("password123")
print(f"Trovata {count} volte nei leak" if count else "Non trovata")
# Output reale (verificato il 20 agosto 2026): Trovata 2266543 volte nei leak

Nota l’header Add-Padding: true: aggiunge righe fittizie con conteggio zero alla risposta, in modo che un osservatore che intercetta il traffico di rete non possa dedurre nulla dalla dimensione della risposta stessa. È un dettaglio di sicurezza raccomandato dalla documentazione ufficiale e che spesso viene dimenticato in fretta dagli sviluppatori.

Passo 4: ottenere una chiave API v3 per la ricerca via email

Per cercare violazioni associate a un indirizzo email o a un dominio serve una sottoscrizione a pagamento. Vai su haveibeenpwned.com/API/v3, crea un account e scegli un piano dalla pagina delle sottoscrizioni. I piani “Core”, pensati per l’uso individuale o per piccoli team, sono così strutturati (dati verificati sulla pagina ufficiale delle sottoscrizioni, agosto 2026):

PianoRichieste/minutoIndirizzi email interrogabiliDomini monitorabiliPrezzo mensile
Core 1102514,39 $
Core 250100321,59 $
Core 3100500536,99 $
Core 4500illimitati10159 $
Core 51.000illimitati10+319 $

Per uso aziendale più intenso esistono i piani “Pro”, che partono da 1.000 richieste al minuto a 379 $/mese e arrivano a 16.000 richieste al minuto a 4.599 $/mese. Per un piccolo team IT o un consulente di sicurezza, Core 1 o Core 2 sono in genere sufficienti. Una volta attivato il piano, la chiave API compare nella tua area riservata: conservala come faresti con qualsiasi altro segreto, per esempio in un vault o in variabili d’ambiente, mai hardcoded nel codice.

Passo 5: interrogare l’API HIBP per un singolo indirizzo email

Con la chiave in mano, il controllo via curl richiede l’header hibp-api-key e uno user agent personalizzato (l’API rifiuta le richieste senza uno user agent riconoscibile):

curl -s -X GET \
  "https://haveibeenpwned.com/api/v3/breachedaccount/mario.rossi%40azienda.it" \
  -H "hibp-api-key: LA_TUA_CHIAVE_API" \
  -H "User-Agent: MyCompany-BreachMonitor/1.0"

# Output di esempio se l'account compare in violazioni:
# [{"Name":"Collection1"},{"Name":"LinkedIn"}]
#
# Output se l'account non compare in nessuna violazione:
# HTTP/1.1 404 Not Found (corpo vuoto)

Da notare due dettagli che confondono spesso chi usa l’API per la prima volta: l’indirizzo email va URL-encoded (la @ diventa %40), e un account “pulito” non restituisce un JSON vuoto ma proprio un errore HTTP 404. Se vuoi anche i dettagli di ogni singola violazione (numero di record, tipo di dati esposti, data), aggiungi il parametro ?truncateResponse=false alla query.

L’API v3 espone anche un endpoint gemello per le “paste”, cioè i frammenti di testo pubblicati su siti come Pastebin che contengono spesso credenziali copiate e incollate da chi le ha rubate: /api/v3/pasteaccount/{email}. Vale la pena interrogarlo insieme alla ricerca per violazione, perché intercetta materiale che non sempre finisce in un breach formale ma che circola comunque tra gli attaccanti.

Passo 6: uno script Python per il controllo automatico di più account

Per un team IT che deve controllare decine di account, ha senso scrivere uno script che legga una lista di email da un file e rispetti il rate limit del piano sottoscritto. Ecco una versione funzionante per il piano Core 1 (10 richieste al minuto, quindi una ogni 6 secondi):

import time
import requests

API_KEY = "LA_TUA_CHIAVE_API"
HEADERS = {"hibp-api-key": API_KEY, "User-Agent": "MyCompany-BreachMonitor/1.0"}
DELAY_SECONDS = 6  # 10 richieste/minuto sul piano Core 1

def check_email(email: str) -> list:
    url = f"https://haveibeenpwned.com/api/v3/breachedaccount/{email}"
    resp = requests.get(url, headers=HEADERS, timeout=10)
    if resp.status_code == 404:
        return []
    resp.raise_for_status()
    return [b["Name"] for b in resp.json()]

def scan_accounts(email_list_path: str):
    with open(email_list_path) as f:
        emails = [line.strip() for line in f if line.strip()]

    for email in emails:
        breaches = check_email(email)
        status = f"{len(breaches)} violazioni: {', '.join(breaches)}" if breaches else "pulito"
        print(f"{email} -> {status}")
        time.sleep(DELAY_SECONDS)

if __name__ == "__main__":
    scan_accounts("accounts.txt")

Con un elenco di 50 account e un piano Core 1, il controllo completo richiede circa 5 minuti (50 × 6 secondi). Se il team è più numeroso, conviene passare a un piano Core 2 o Core 3 per ridurre i tempi di attesa.

Passo 7: integrare il controllo password in un form Node.js/Express

Il caso d’uso più efficace di Pwned Passwords non è il controllo occasionale, ma il blocco preventivo in fase di registrazione: se un utente sceglie una password già comparsa migliaia di volte nei leak, meglio dirglielo prima che dopo. Ecco un middleware Express che usa l’endpoint gratuito senza mai inviare la password in chiaro:

import crypto from "node:crypto";
import express from "express";

const app = express();
app.use(express.json());

async function isPasswordPwned(password) {
  const sha1 = crypto.createHash("sha1").update(password).digest("hex").toUpperCase();
  const prefix = sha1.slice(0, 5);
  const suffix = sha1.slice(5);

  const res = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`, {
    headers: { "Add-Padding": "true" },
  });
  const text = await res.text();

  for (const line of text.split("\r\n")) {
    const [hashSuffix, count] = line.split(":");
    if (hashSuffix === suffix) return parseInt(count, 10);
  }
  return 0;
}

app.post("/register", async (req, res) => {
  const { password } = req.body;
  const pwnCount = await isPasswordPwned(password);

  if (pwnCount > 0) {
    return res.status(400).json({
      error: `Questa password è comparsa ${pwnCount} volte in violazioni note. Scegline un'altra.`,
    });
  }

  // procedi con la creazione dell'account e l'hashing con bcrypt/argon2
  res.status(201).json({ ok: true });
});

app.listen(3000, () => console.log("Server avviato sulla porta 3000"));

Il controllo va sempre eseguito prima dell’hashing definitivo della password (con bcrypt o Argon2, come spiegato nella nostra guida all’hashing password con bcrypt in Node.js), non al posto di esso: Pwned Passwords ti dice se una password è già nota agli attaccanti, l’hashing protegge il database in caso di violazione futura. Sono due livelli di difesa complementari, non alternativi.

Passo 8: monitorare un dominio aziendale con la Domain Search API

Se gestisci un dominio aziendale (per esempio azienda.it), HIBP offre un endpoint dedicato che restituisce tutti gli indirizzi email di quel dominio comparsi in violazioni note, senza doverli controllare uno per uno. Prima serve verificare la proprietà del dominio (HIBP chiede di inserire un record TXT DNS o un file di verifica), poi puoi interrogare l’endpoint:

curl -s -X GET \
  "https://haveibeenpwned.com/api/v3/breacheddomain/azienda.it" \
  -H "hibp-api-key: LA_TUA_CHIAVE_API" \
  -H "User-Agent: MyCompany-BreachMonitor/1.0"

# Output di esempio:
# {
#   "alice": ["Collection1", "LinkedIn"],
#   "bob": ["OnlinerSpambot"]
# }

Il vantaggio rispetto al controllo account per account è enorme: con una sola chiamata ottieni la mappa completa degli account aziendali esposti, comprese caselle dismesse o dimenticate che magari nessuno controlla più ma che restano un punto di ingresso valido per un attaccante. Per i piani Core serve un numero minimo di indirizzi corrispondente al piano sottoscritto (per esempio Core 1 copre fino a 25 indirizzi email verificabili sul dominio).

Passo 9-10: automatizzare gli avvisi e gestire il rate limiting

Un controllo manuale ripetuto ogni tanto è meglio di niente, ma il vero valore arriva quando il sistema ti avvisa da solo appena compare una nuova violazione. Ecco uno script che unisce il controllo del dominio a una notifica su Slack via webhook, gestendo anche gli errori di rate limit (HTTP 429):

import time
import requests

HIBP_KEY = "LA_TUA_CHIAVE_API"
SLACK_WEBHOOK = "https://hooks.slack.com/services/TUO/WEBHOOK/QUI"
DOMAIN = "azienda.it"

def get_breached_domain_accounts(domain: str, max_retries: int = 3) -> dict:
    url = f"https://haveibeenpwned.com/api/v3/breacheddomain/{domain}"
    headers = {"hibp-api-key": HIBP_KEY, "User-Agent": "MyCompany-BreachMonitor/1.0"}

    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers, timeout=10)
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 10))
            print(f"Rate limit raggiunto, attendo {retry_after}s...")
            time.sleep(retry_after)
            continue
        resp.raise_for_status()
        return resp.json()
    raise RuntimeError("Rate limit persistente dopo i tentativi previsti")

def notify_slack(message: str):
    requests.post(SLACK_WEBHOOK, json={"text": message}, timeout=10)

def run_check():
    accounts = get_breached_domain_accounts(DOMAIN)
    if not accounts:
        print("Nessun account esposto trovato.")
        return

    lines = [f"*{len(accounts)} account di {DOMAIN} coinvolti in violazioni:*"]
    for alias, breaches in accounts.items():
        lines.append(f"- {alias}@{DOMAIN}: {', '.join(breaches)}")

    notify_slack("\n".join(lines))

if __name__ == "__main__":
    run_check()

Il codice HTTP 429 significa semplicemente che hai superato il numero di richieste consentite dal tuo piano nell’intervallo di tempo previsto: l’header Retry-After ti dice esattamente quanti secondi aspettare prima di riprovare. Ignorarlo e ripetere subito la richiesta è il modo più veloce per farsi bloccare temporaneamente la chiave API.

Per ambienti con più script che condividono la stessa chiave (per esempio un controllo email e un controllo dominio eseguiti da job separati), conviene centralizzare le chiamate API dietro una coda unica con un contatore condiviso, invece di lasciare che ogni script gestisca il proprio rate limit in modo indipendente. In caso contrario, due processi che partono nello stesso minuto possono superare insieme il limite anche se ciascuno, da solo, resterebbe entro la soglia.

Passo 11-12: piano di risposta a una violazione e controlli periodici con cron

Trovare un account compromesso è solo metà del lavoro. Quando lo script segnala un match, segui questa sequenza:

  • Forza subito il reset della password dell’account coinvolto, non limitarti ad avvisare l’utente
  • Revoca le sessioni attive e i token API associati a quell’account
  • Se la violazione riguarda dati oltre alla password (numeri di telefono, indirizzi, dati sanitari), valuta con il DPO se scattano gli obblighi di notifica GDPR entro 72 ore verso il Garante per la protezione dei dati personali
  • Controlla se la stessa password (anche con piccole variazioni) è riusata su altri sistemi interni
  • Documenta l’incidente: fonte della violazione, data di rilevamento, azioni intraprese. Ti serve sia per la conformità NIS2 sia per capire pattern ricorrenti

Un punto spesso sottovalutato: non tutte le violazioni segnalate da HIBP richiedono una notifica formale al Garante. L’articolo 33 del GDPR fa scattare l’obbligo delle 72 ore solo quando la violazione comporta un rischio per i diritti e le libertà delle persone fisiche, e solo se riguarda dati sotto il tuo controllo diretto come titolare del trattamento. Se lo script segnala che l’email aziendale di un dipendente compare in un vecchio leak di un servizio terzo (per esempio un forum a cui si era iscritto anni prima), non è automaticamente un incidente da notificare: è comunque un segnale da usare per forzare il reset della password, ma la valutazione formale spetta al DPO caso per caso.

Per non dipendere da un controllo manuale, pianifica lo script di monitoraggio con cron. Un controllo giornaliero è un buon compromesso tra tempestività e consumo della quota API:

# Esegue il controllo ogni giorno alle 07:00
0 7 * * * /usr/bin/python3 /opt/breach-monitor/check_domain.py >> /var/log/breach-monitor.log 2>&1

Questo approccio si integra bene con un piano di incident response più ampio: se non ne hai ancora uno, la nostra guida al piano di incident response CSIRT copre la struttura completa, dal rilevamento alla chiusura dell’incidente.

Errori comuni da evitare

Nel costruire un sistema di monitoraggio basato su HIBP, questi sono gli errori che vediamo più spesso, in ordine di frequenza:

  1. Confondere Pwned Passwords con la ricerca per email. Sono due servizi diversi: il primo è gratuito e anonimo, il secondo richiede una chiave API a pagamento. Molti sviluppatori scoprono la differenza solo dopo aver ricevuto un 401 Unauthorized.
  2. Inviare la password in chiaro al posto dell’hash. Se il tuo codice invia la password intera a un endpoint remoto, invece di calcolare l’hash localmente e mandare solo il prefisso, stai vanificando tutto il vantaggio di privacy del k-anonymity.
  3. Dimenticare lo User-Agent nelle chiamate API. L’API HIBP rifiuta le richieste senza uno user agent identificabile, restituendo un errore poco chiaro se non sai dove guardare.
  4. Non gestire il 404 come risposta valida. Un account “pulito” restituisce HTTP 404, non un JSON con un array vuoto. Un parsing che si aspetta sempre un body JSON va in errore.
  5. Bloccare l’account invece di forzare solo il reset password. Sospendere un utente perché la sua email compare in un vecchio leak (magari di un servizio terzo, non collegato alla tua password attuale) genera solo attrito. Il controllo serve a prevenire, non a punire.
  6. Ignorare il rate limit e sommergere l’API di richieste parallele. Oltre a inutilizzare il piano acquistato, rischi la sospensione temporanea della chiave.
  7. Trattare un risultato “pulito” come garanzia definitiva. HIBP cataloga solo le violazioni che il team riesce a raccogliere e verificare: un account non presente nel database non è necessariamente al sicuro, potrebbe far parte di un leak non ancora scoperto o non ancora reso pubblico.

La maggior parte di questi errori nasce dalla stessa causa: trattare l’API di HIBP come un servizio generico, senza leggere le specifiche di autenticazione, rate limit e formato delle risposte prima di scrivere il codice di produzione. I dieci minuti spesi a leggere la documentazione ufficiale prima di iniziare valgono più di un’ora di debug successivo.

Risoluzione dei problemi

Ecco gli errori più comuni che incontrerai lavorando con l’API, con la causa reale e la soluzione. Tienila a portata di mano durante i primi test: la maggior parte dei problemi rientra in una di queste otto categorie, ed è più veloce controllare la tabella che aprire un ticket di supporto.

ProblemaCausa probabileSoluzione
HTTP 401 UnauthorizedChiave API mancante, scaduta o errata nell’headerVerifica l’header hibp-api-key e lo stato della sottoscrizione nell’area utente
HTTP 403 ForbiddenManca lo User-Agent nella richiestaAggiungi sempre un header User-Agent descrittivo alle chiamate
HTTP 404 su un account pulitoComportamento normale, non un erroreNel codice, tratta il 404 come “nessuna violazione trovata”, non come eccezione
HTTP 429 Too Many RequestsSuperato il rate limit del piano sottoscrittoRispetta l’header Retry-After e valuta un piano superiore se il volume cresce
Risposta vuota su Pwned PasswordsPrefisso hash calcolato in modo errato (es. hash non maiuscolo)Verifica che l’hash SHA-1 sia in maiuscolo prima di estrarre prefisso e suffisso
Il dominio non si verificaRecord TXT DNS non ancora propagatoAttendi la propagazione DNS (può richiedere alcune ore) prima di ripetere la verifica
Timeout sulle chiamate batchTroppe richieste in sequenza senza pausaInserisci un delay coerente con il rate limit, come nello script del Passo 6
Falsi negativi su password “quasi” compromesseIl servizio verifica hash esatti, non varianti con maiuscole o numeri finali diversiNon affidarti solo al controllo esatto: applica anche regole di complessità minima lato applicazione

Consigli avanzati

Una volta che il sistema base funziona, alcuni accorgimenti fanno la differenza tra un controllo “fatto una volta” e un processo di sicurezza affidabile.

Primo: non limitarti a HIBP come unica fonte. Il servizio è eccellente ma copre solo le violazioni che il team di Troy Hunt riesce a raccogliere e verificare. Integra il controllo con un secondo fattore di autenticazione obbligatorio (vedi la nostra guida su TOTP 2FA in Node.js), così anche se una password risulta compromessa l’account resta protetto.

Secondo: se il volume di controlli cresce oltre le poche centinaia di account, valuta il download offline del set completo di Pwned Passwords (disponibile per il download diretto sul sito), così puoi eseguire i controlli localmente senza consumare quota API e senza dipendere dalla disponibilità del servizio remoto.

Terzo: collega il monitoraggio HIBP al tuo password manager aziendale. Molti gestori di password moderni (ne parliamo nel confronto tra Bitwarden e KeePassXC) integrano già controlli simili, ma un livello aggiuntivo di monitoraggio a livello di dominio aziendale copre anche gli account che non passano dal password manager centralizzato.

Quarto: se operi in un settore regolato da NIS2 o gestisci dati sanitari o finanziari, documenta il processo di controllo HIBP come parte della tua policy di gestione delle vulnerabilità. In caso di audit, dimostrare un monitoraggio proattivo e continuo pesa a tuo favore.

Quinto: per gli account più critici (amministratori di dominio, accessi VPN, caselle usate per il recupero password di altri sistemi), non fermarti al controllo email. La Domain Search API più recente include anche una ricerca nei cosiddetti “stealer log”, cioè i dati raccolti da malware che rubano credenziali direttamente dal browser della vittima, spesso senza che sia avvenuto un vero e proprio breach del servizio online. È un segnale di compromissione ancora più diretto rispetto a un leak aziendale, perché indica che il dispositivo dell’utente, non solo la sua password, è stato infettato.

Progetto completo: monitor aziendale pronto all’uso

Ecco lo script completo che unisce tutti i passi precedenti in un unico strumento: controlla il dominio, gestisce il rate limit, invia una notifica Slack solo se trova novità rispetto all’ultimo controllo (per evitare notifiche ripetute), e si può schedulare con cron.

import json
import os
import time
import requests

HIBP_KEY = os.environ["HIBP_API_KEY"]
SLACK_WEBHOOK = os.environ["SLACK_WEBHOOK_URL"]
DOMAIN = os.environ.get("MONITORED_DOMAIN", "azienda.it")
STATE_FILE = "last_known_breaches.json"
HEADERS = {"hibp-api-key": HIBP_KEY, "User-Agent": "MyCompany-BreachMonitor/1.0"}

def load_previous_state() -> dict:
    if os.path.exists(STATE_FILE):
        with open(STATE_FILE) as f:
            return json.load(f)
    return {}

def save_state(state: dict):
    with open(STATE_FILE, "w") as f:
        json.dump(state, f, indent=2)

def fetch_domain_accounts(domain: str, max_retries: int = 3) -> dict:
    url = f"https://haveibeenpwned.com/api/v3/breacheddomain/{domain}"
    for attempt in range(max_retries):
        resp = requests.get(url, headers=HEADERS, timeout=10)
        if resp.status_code == 404:
            return {}
        if resp.status_code == 429:
            wait = int(resp.headers.get("Retry-After", 15))
            time.sleep(wait)
            continue
        resp.raise_for_status()
        return resp.json()
    raise RuntimeError("Impossibile completare la richiesta dopo i tentativi previsti")

def find_new_entries(previous: dict, current: dict) -> dict:
    new_entries = {}
    for alias, breaches in current.items():
        old_breaches = set(previous.get(alias, []))
        new_breaches = [b for b in breaches if b not in old_breaches]
        if new_breaches:
            new_entries[alias] = new_breaches
    return new_entries

def notify_slack(new_entries: dict):
    lines = [f"*Nuove violazioni rilevate su {DOMAIN}:*"]
    for alias, breaches in new_entries.items():
        lines.append(f"- {alias}@{DOMAIN}: {', '.join(breaches)}")
    requests.post(SLACK_WEBHOOK, json={"text": "\n".join(lines)}, timeout=10)

def main():
    previous_state = load_previous_state()
    current_state = fetch_domain_accounts(DOMAIN)
    new_entries = find_new_entries(previous_state, current_state)

    if new_entries:
        print(f"Trovate novità per {len(new_entries)} account, invio notifica.")
        notify_slack(new_entries)
    else:
        print("Nessuna novità rispetto all'ultimo controllo.")

    save_state(current_state)

if __name__ == "__main__":
    main()

Salva lo script, imposta le variabili d’ambiente HIBP_API_KEY, SLACK_WEBHOOK_URL e MONITORED_DOMAIN, e collegalo a cron come mostrato nel Passo 11-12. Il file di stato locale evita notifiche duplicate: riceverai un messaggio solo quando compare davvero qualcosa di nuovo, non a ogni esecuzione pianificata.

Per un contesto di riferimento su quanto la scala del problema sia cresciuta, lo scorso anno abbiamo coperto anche il leak da 16 miliardi di credenziali che ha coinvolto compilation simili a SynthientCredentialStuffingThreatData: uno script come questo è esattamente il tipo di automazione che avrebbe accorciato i tempi di reazione in quel caso.

Domande frequenti

Have I Been Pwned è sicuro da usare?
Sì. Il controllo delle password usa il modello k-anonymity, quindi la password intera non lascia mai il tuo dispositivo. Il controllo email invia solo l’indirizzo, non credenziali, e il progetto è gestito da un ricercatore di sicurezza indipendente con oltre dieci anni di attività pubblica e trasparente.

Have I Been Pwned è gratuito?
Il controllo di una singola password tramite Pwned Passwords è gratuito e senza limiti pratici. La ricerca per indirizzo email o dominio tramite API richiede invece una sottoscrizione, a partire da 4,39 $ al mese per il piano Core 1.

Cosa devo fare se trovo il mio account in una violazione?
Cambia subito la password su quel servizio e su qualsiasi altro dove l’hai riusata, attiva l’autenticazione a due fattori se non l’hai già fatta, e controlla se la violazione ha esposto anche altri dati sensibili che richiedono ulteriori azioni.

Quante violazioni sono presenti oggi nel database HIBP?
Alla data del 20 agosto 2026, il catalogo pubblico conta 1.029 violazioni, di cui 987 verificate e 77 aggiunte nel solo 2026.

L’API HIBP ha un limite di richieste?
Sì, ogni piano ha un rate limit espresso in richieste al minuto: si parte da 10 richieste/minuto sul piano Core 1 fino a 16.000 richieste/minuto sui piani Pro più alti. Superare il limite genera una risposta HTTP 429.

Posso usare HIBP per verificare interi elenchi di dipendenti?
Sì, tramite la Domain Search API, dopo aver verificato la proprietà del dominio aziendale con un record DNS. È il metodo più efficiente per un controllo su larga scala, molto più rapido del controllo account per account.

Have I Been Pwned sostituisce un piano di incident response?
No. È uno strumento di rilevamento precoce, non un sostituto di un processo strutturato. Va integrato in un piano più ampio che definisca chi agisce, come e in quali tempi quando arriva un avviso.

Perché la password “password” continua a comparire nei leak dopo anni?
Perché il database di Pwned Passwords aggrega hash da centinaia di violazioni storiche e recenti: ogni volta che qualcuno, in un servizio qualsiasi, sceglie ancora “password” come credenziale, quel comportamento resta cristallizzato nei dati aggregati anche a distanza di anni.

Qual è la violazione più grande mai registrata da Have I Been Pwned?
È SynthientCredentialStuffingThreatData, una compilation da 1.957.476.021 credenziali datata 11 aprile 2025 e aggiunta al catalogo il 6 novembre 2025. Non è un breach di un singolo servizio ma un’aggregazione di materiale già rubato, usata per attacchi automatizzati di credential stuffing su larga scala.

Serve installare software per usare Have I Been Pwned?
No. Il controllo base funziona interamente dal browser, senza installazioni. Per l’automazione via API basta un linguaggio in grado di fare richieste HTTP, come Python o Node.js, entrambi usati negli script di questa guida.

Copertura correlata