Verificare se una password è stata compromessa con Python e Have I Been Pwned

  

Quando parliamo di sicurezza informatica, una delle domande più comuni è:

La mia password è mai finita in una fuga di dati?

Il servizio Have I Been Pwned (HIBP) (ne abbiamo parlato qui), creato da Troy Hunt, raccoglie miliardi di credenziali compromesse provenienti da data breach reali. Fortunatamente, mette a disposizione un’API che consente di verificare una password senza mai inviarla in chiaro (e nemmeno per intero).

In questo articolo realizzeremo insieme al lettore un piccolo programma Python che utilizza questa API in modo sicuro, sfruttando la tecnica chiamata k-anonymity basata su hash.

🔗 Ti piace Techelopment? Dai un'occhiata al sito per tutti i dettagli!

⚠️ Perché non bisogna mai inviare una password

Inviare una password a un servizio esterno, anche se promette di non salvarla, è sempre una pessima idea. Una buona API di sicurezza deve permettere il controllo senza conoscere il segreto.

HIBP risolve il problema con un approccio elegante:

  • la password viene hashata localmente (SHA-1)
  • solo i primi 5 caratteri dell’hash vengono inviati al server
  • il server restituisce una lista di hash parziali
  • il confronto finale avviene sul nostro computer

Il server non saprà mai quale password stiamo controllando.


🔐 La tecnica: k-anonymity con hash SHA-1

Il flusso è questo:

  1. L’utente inserisce la password
  2. Python calcola l’hash SHA-1
  3. Dividiamo l’hash in due parti:
    • prefisso: primi 5 caratteri
    • suffisso: il resto
  4. Inviamo solo il prefisso all’API
  5. L’API risponde con migliaia di suffissi possibili
  6. Verifichiamo localmente se il nostro suffisso è presente

In questo modo, la nostra password è anonima tra almeno migliaia di altre.


🧪 Prepariamo l’ambiente

Useremo solo librerie standard più requests.

pip install requests

🧠 Step 1 – Calcolare l’hash SHA-1 della password

import hashlib

def sha1_hash(password: str) -> str:
    return hashlib.sha1(password.encode('utf-8')).hexdigest().upper()

Nota importante:
- l’hash deve essere in maiuscolo, come richiesto dall’API HIBP


🌐 Step 2 – Chiamare l’API di Have I Been Pwned

L’endpoint da usare è:

https://api.pwnedpasswords.com/range/{HASH_PREFIX}

Ecco la funzione che effettua la richiesta:

import requests

def get_pwned_hashes(prefix: str) -> str:
    url = f"https://api.pwnedpasswords.com/range/{prefix}"
    response = requests.get(url)
    response.raise_for_status()
    return response.text

La risposta sarà una stringa simile a:

0033A1D3F0A1E3C...:2
00A1B2C3D4E5F6...:154

Ogni riga indica quante volte quella password è apparsa in un data breach.


🔍 Step 3 – Verificare se la password è stata compromessa

Ora mettiamo tutto insieme:

def check_password(password: str) -> int:
    full_hash = sha1_hash(password)
    prefix = full_hash[:5]
    suffix = full_hash[5:]

    hashes = get_pwned_hashes(prefix)

    for line in hashes.splitlines():
        hash_suffix, count = line.split(':')
        if hash_suffix == suffix:
            return int(count)

    return 0

La funzione restituisce:
- 0 se la password non è mai stata trovata
- un numero > 0 se è stata compromessa


🖥️ Step 4 – Un semplice programma completo

if __name__ == "__main__":
    password = input("Inserisci la password da verificare: ")
    count = check_password(password)

    if count:
        print(f"⚠️ Password trovata {count} volte in data breach noti!")
    else:
        print("✅ Password non trovata nei database di Have I Been Pwned")

💡 Suggerimento: per maggiore sicurezza, usa getpass.getpass() invece di input().


🛡️ Cosa non garantisce questo controllo

È importante essere onesti:

  • una password non trovata non è automaticamente sicura
  • SHA-1 non è adatto per memorizzare password (ma qui è solo un confronto)
  • una password può essere debole anche se non è mai stata violata

Questo strumento serve a evitare password già compromesse, non a validarne la qualità.


🚀 Spunti di miglioramento: nascondere la password durante l’input

Nel programma di esempio abbiamo utilizzato input() per semplicità, ma questo comporta un problema evidente: la password viene mostrata in chiaro mentre viene digitata.

In un contesto reale, anche solo locale, è una cattiva abitudine perché espone la password a:

  • persone che guardano lo schermo (shoulder surfing)
  • registrazioni dello schermo
  • log accidentali durante debug o demo

🔒 La soluzione: getpass

Python mette a disposizione il modulo standard getpass, progettato proprio per questo scenario. Permette di leggere una password da tastiera senza mostrarla a schermo.

Ecco come integrarlo:

import getpass

password = getpass.getpass("Inserisci la password da verificare: ")

Il resto del programma può rimanere invariato.

✅ Vantaggi di getpass

  • la password non viene visualizzata
  • funziona su Linux, macOS e Windows
  • non richiede librerie esterne
  • è lo standard de facto per input sensibili in CLI

⚠️ Nota importante

getpass nasconde l’input, ma non rende la password più sicura in memoria.
Per applicazioni più avanzate, si potrebbero considerare ulteriori accorgimenti, come:

  • cancellare la variabile dopo l’uso
  • limitare il tempo di permanenza in memoria
  • usare ambienti isolati o secure input hardware

Per uno script di verifica come quello visto in questo articolo, getpass rappresenta comunque un netto miglioramento in termini di sicurezza e professionalità.


✅ Conclusione

Con poche righe di Python abbiamo realizzato:

  • un controllo di sicurezza reale
  • senza mai trasmettere la password
  • usando un’API pubblica e affidabile
  • applicando un principio crittografico elegante

È un ottimo esempio di come sicurezza e privacy possano convivere quando il design è fatto bene.

Buon hacking… etico 😉

Techelopment ha già pensato a tutto 😊

👉 Il programma completo mostrato nell’articolo, comprensivo di tutti gli esempi e delle migliorie discusse, è disponibile su GitHub, così da poter essere scaricato, eseguito e adattato facilmente alle proprie esigenze.



Follow me #techelopment

Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment