SHA-256 in Python — Guida hashlib con esempi
Usa il Generatore Hash SHA-256 gratuito direttamente nel tuo browser — nessuna installazione.
Prova Generatore Hash SHA-256 online →Ogni pipeline di deployment che ho costruito ha prima o poi bisogno di verificare il checksum di un file, firmare il payload di un webhook o ricavare una chiave di cache. L'hashing SHA-256 in Python con il modulo integrato hashlib gestisce tutti questi casi — ed è già disponibile nell'installazione standard. hashlib.sha256() delega all'implementazione C di OpenSSL su CPython, quindi è veloce e conforme FIPS out of the box. Per un hash rapido senza scrivere codice, il generatore SHA-256 online fornisce il risultato all'istante. Tutti gli esempi sono per Python 3.9+.
- ✓hashlib.sha256(data).hexdigest() è il metodo standard per calcolare l'hash di bytes — parte della stdlib, basato su OpenSSL.
- ✓Le stringhe devono essere prima codificate in bytes: hashlib.sha256("testo".encode("utf-8")).
- ✓Per i checksum di file, si alimentano blocchi tramite .update() — non caricare mai un file di grandi dimensioni interamente in memoria.
- ✓HMAC-SHA256 richiede il modulo hmac: hmac.new(key, msg, hashlib.sha256) — SHA-256 da solo non ha una chiave.
Cos'è l'hashing SHA-256?
SHA-256 (Secure Hash Algorithm, 256 bit) prende un input di lunghezza arbitraria e produce un digest fisso di 256 bit (32 byte). Lo stesso input produce sempre lo stesso output, ma anche un singolo bit di differenza nell'input genera un hash completamente diverso — questa proprietà si chiama effetto valanga. SHA-256 fa parte della famiglia SHA-2, standardizzata dal NIST, ed è alla base delle impronte dei certificati TLS, degli ID di commit Git, delle intestazioni dei blocchi Bitcoin e della verifica dell'integrità dei file. L'algoritmo usa una costruzione Merkle-Damgård con 64 round di compressione per produrre il suo output di 256 bit.
deployment-v4.2.1
a1f7c3d8e9b2...27ae41e4649b (64 caratteri hex)
Il digest esadecimale sopra è la rappresentazione standard — 64 caratteri esadecimali, sempre della stessa lunghezza indipendentemente dal fatto che si stia calcolando l'hash di un singolo byte o di un'intera immagine disco.
hashlib.sha256() — L'approccio della libreria standard
Il modulo hashlib è incluso in ogni installazione Python — nessun pip install necessario. Si chiama hashlib.sha256() con un argomento di tipo bytes per creare un oggetto hash, quindi si ottiene il risultato con .hexdigest() (stringa esadecimale) o .digest() (byte grezzi). Il nome della funzione è in minuscolo: sha256, non SHA256.
import hashlib # Hash diretto di una stringa di byte digest = hashlib.sha256(b"deployment-v4.2.1").hexdigest() print(digest) # a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
L'errore più comune con hashlib.sha256() è passare una str invece di bytes. Le stringhe Python sono Unicode e le funzioni hash operano su byte grezzi. È necessario chiamare .encode("utf-8") prima dell'hashing. Questo è l'errore che fa inciampare quasi tutti la prima volta.
import hashlib
# Le stringhe devono essere codificate in bytes prima dell'hashing
config_key = "redis://cache.internal:6379/0"
digest = hashlib.sha256(config_key.encode("utf-8")).hexdigest()
print(digest)
# 7d3f8c2a1b9e4f5d6c8a7b3e2f1d9c4a5b8e7f6d3c2a1b9e4f5d6c8a7b3e2f1dIl metodo .update() permette di alimentare dati in modo incrementale. Chiamare h.update(a); h.update(b) è equivalente a hashlib.sha256(a + b). Questo è il modo per calcolare l'hash di file a blocchi senza caricare l'intero contenuto in memoria.
import hashlib h = hashlib.sha256() h.update(b"request_id=req_7f3a91bc") h.update(b"×tamp=1741614120") h.update(b"&amount=4999") print(h.hexdigest()) # Equivalente a hashlib.sha256(b"request_id=req_7f3a91bc×tamp=1741614120&amount=4999").hexdigest()
.digest() restituisce i 32 byte grezzi. .hexdigest() restituisce una stringa esadecimale di 64 caratteri. Usare .digest() quando si alimenta il risultato a HMAC, codifica Base64 o protocolli binari. Usare .hexdigest() per log, colonne di database e confronto di checksum.HMAC-SHA256 — Hashing con chiave tramite il modulo hmac
SHA-256 da solo non prevede una chiave segreta — chiunque abbia lo stesso input può calcolare lo stesso hash. Se si ha bisogno di dimostrare che un messaggio proviene da un mittente specifico (verifica webhook, firma di richieste API, autenticazione token), è necessario HMAC. Il modulo hmac fa parte della libreria standard di Python e incorpora la chiave nel processo di hashing in modo che solo chi possiede la chiave possa produrre o verificare lo stesso digest.
import hmac
import hashlib
# Verifica della firma webhook
secret_key = b"whsec_9f3a7b2e1d4c8a5b"
payload = b'{"event":"invoice.paid","invoice_id":"inv_8d2c","amount":14900}'
signature = hmac.new(secret_key, payload, hashlib.sha256).hexdigest()
print(signature)
# Digest HMAC-SHA256 esadecimale di 64 caratteriPer verificare un HMAC in arrivo è necessario usare hmac.compare_digest() invece dell'operatore ==. L'operatore di uguaglianza è vulnerabile ad attacchi temporali — si interrompe al primo byte diverso e un attaccante può misurare i tempi di risposta per indovinare la firma corretta byte per byte. compare_digest() viene eseguito in tempo costante indipendentemente da dove si verifica la discrepanza.
import hmac
import hashlib
def verify_webhook(payload: bytes, received_sig: str, secret: bytes) -> bool:
"""Verifica una firma webhook usando il confronto in tempo costante."""
expected = hmac.new(secret, payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_sig)
# Simulazione di una verifica webhook in stile Stripe
incoming_payload = b'{"event":"payment.completed","amount":4999}'
incoming_signature = "a1b2c3d4e5f6..." # dall'header X-Signature
webhook_secret = b"whsec_9f3a7b2e1d4c"
if verify_webhook(incoming_payload, incoming_signature, webhook_secret):
print("Firma valida — elabora l'evento")
else:
print("Firma non corrispondente — rifiuta la richiesta")Firma delle richieste API con HMAC-SHA256
La firma delle richieste API segue lo stesso principio: si costruisce una stringa canonica dalle componenti della richiesta (metodo, percorso, timestamp, hash del corpo) e la si firma con la propria chiave segreta. AWS Signature V4, Stripe e i webhook GitHub usano tutti varianti di questo schema.
import hmac
import hashlib
import time
def sign_request(method: str, path: str, body: bytes, secret: bytes) -> str:
"""Crea una firma HMAC-SHA256 per una richiesta API."""
timestamp = str(int(time.time()))
body_hash = hashlib.sha256(body).hexdigest()
# Stringa canonica: metodo + percorso + timestamp + hash del corpo
canonical = f"{method}\n{path}\n{timestamp}\n{body_hash}"
signature = hmac.new(secret, canonical.encode("utf-8"), hashlib.sha256).hexdigest()
return f"ts={timestamp},sig={signature}"
# Utilizzo
api_secret = b"sk_live_9f3a7b2e1d4c8a5b6e7f"
request_body = b'{"customer_id":"cust_4f2a","plan":"enterprise"}'
auth_header = sign_request("POST", "/api/v2/subscriptions", request_body, api_secret)
print(f"Authorization: HMAC-SHA256 {auth_header}")
# Authorization: HMAC-SHA256 ts=1741614120,sig=7d3f8c2a...HMAC-SHA256 codificato in Base64
Alcune API (AWS Signature V4, vari gateway di pagamento) si aspettano il risultato HMAC come stringa Base64 anziché esadecimale. La differenza: il formato hex usa 64 caratteri, Base64 ne usa 44 per lo stesso digest di 32 byte.
import hmac
import hashlib
import base64
secret = b"webhook_secret_9f3a"
message = b"POST /api/v2/events 1741614120"
# Output esadecimale: 64 caratteri
hex_sig = hmac.new(secret, message, hashlib.sha256).hexdigest()
print(f"Hex: {hex_sig}")
# Output Base64: 44 caratteri (più corto, comune negli header HTTP)
raw_sig = hmac.new(secret, message, hashlib.sha256).digest()
b64_sig = base64.b64encode(raw_sig).decode("ascii")
print(f"Base64: {b64_sig}")Hashing di datetime, UUID e oggetti personalizzati
SHA-256 opera su byte grezzi, quindi i tipi non-bytes — datetime, UUID, dataclass, modelli Pydantic — devono essere serializzati in bytes prima dell'hashing. Non esiste conversione automatica; si sceglie la rappresentazione canonica. Per un hashing deterministico tra sistemi diversi, usare sempre una codifica esplicita e un formato di serializzazione stabile (ISO 8601 per i datetime, la forma stringa standard per gli UUID, JSON con chiavi ordinate per i dizionari).
import hashlib
import uuid
from datetime import datetime, timezone
# datetime — usare ISO 8601 con offset UTC esplicito per portabilità
event_time = datetime(2026, 3, 28, 12, 0, 0, tzinfo=timezone.utc)
time_hash = hashlib.sha256(event_time.isoformat().encode("utf-8")).hexdigest()
print(f"hash datetime: {time_hash[:16]}...")
# UUID — hash della forma stringa canonica (minuscola, con trattini)
record_id = uuid.uuid4()
uuid_hash = hashlib.sha256(str(record_id).encode("utf-8")).hexdigest()
print(f"hash UUID: {uuid_hash[:16]}...")Per oggetti personalizzati, serializzare in una rappresentazione canonica di bytes prima dell'hashing. JSON con chiavi ordinate funziona bene per oggetti simili a dizionari:
import hashlib
import json
from dataclasses import dataclass, asdict
@dataclass
class Evento:
id: str
type: str
amount: int
timestamp: str
def hash_evento(evento: Evento) -> str:
"""Hash di un'istanza dataclass usando JSON con chiavi ordinate per il determinismo."""
canonical = json.dumps(asdict(evento), sort_keys=True, separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
e = Evento(id="evt_4f2a", type="payment.completed", amount=4999, timestamp="2026-03-28T12:00:00Z")
print(hash_evento(e)) # stabile tra esecuzioni e macchine diversesort_keys=True) quando si calcola l'hash di oggetti serializzati in JSON. L'ordine di inserimento nei dizionari è preservato in Python 3.7+, ma può differire tra percorsi di serializzazione diversi, producendo hash diversi per dati identici.Checksum SHA-256 dei file — Verifica di download e artefatti
Il calcolo del checksum SHA-256 di un file è uno degli usi più comuni dell'algoritmo. Lo si incontra ovunque: pagine di release per i binari Go, wheel Python, manifest di immagini Docker, aggiornamenti firmware. Il punto chiave è leggere il file a blocchi anziché caricarlo tutto in una volta — un'immagine ISO da 2 GB non dovrebbe richiedere 2 GB di RAM solo per calcolarne l'hash.
import hashlib
def sha256_checksum(filepath: str, chunk_size: int = 8192) -> str:
"""Calcola l'hash SHA-256 di un file leggendo a blocchi per risparmiare memoria."""
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(chunk_size), b""):
h.update(chunk)
return h.hexdigest()
# Hash di un artefatto di release
checksum = sha256_checksum("/tmp/release-v4.2.1.tar.gz")
print(f"SHA-256: {checksum}")Python 3.11 ha introdotto hashlib.file_digest(), che gestisce internamente la lettura a blocchi e può usare ottimizzazioni zero-copy sulle piattaforme supportate. Se si utilizza la versione 3.11 o successiva, preferire questa alla lettura manuale a ciclo.
import hashlib
with open("/tmp/release-v4.2.1.tar.gz", "rb") as f:
digest = hashlib.file_digest(f, "sha256")
print(digest.hexdigest())Verifica di un file scaricato confrontandolo con un checksum noto
import hashlib
import hmac as hmac_mod # solo per compare_digest
def verify_checksum(filepath: str, expected_hex: str) -> bool:
"""Verifica il checksum SHA-256 usando il confronto in tempo costante."""
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return hmac_mod.compare_digest(h.hexdigest(), expected_hex.lower())
# Verifica di un artefatto di release
expected = "a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db"
if verify_checksum("/tmp/release-v4.2.1.tar.gz", expected):
print("Checksum corrispondente — file integro")
else:
print("Checksum non corrispondente — il file potrebbe essere corrotto o manomesso")hmac.compare_digest() per il confronto dei checksum, anche quando non è coinvolta una chiave segreta. Il confronto in tempo costante previene fughe di informazioni basate sui tempi. L'operatore == funziona dal punto di vista logico, ma non è sicuro nei contesti sensibili alla sicurezza.SHA-256 con codifica Base64
Alcuni protocolli si aspettano il digest SHA-256 come stringa Base64 anziché esadecimale. Gli header HTTP come Content-Digest e Integrity (Subresource Integrity nei browser) usano Base64, e le firme JWT sono codificate in Base64url. Il punto cruciale è codificare in Base64 i byte grezzi di .digest(), non la stringa esadecimale.
import hashlib
import base64
data = b"integrity check payload"
# Corretto: Base64 dei byte grezzi (32 byte → 44 caratteri Base64)
raw_digest = hashlib.sha256(data).digest()
b64_digest = base64.b64encode(raw_digest).decode("ascii")
print(f"sha256-{b64_digest}")
# sha256-<44 caratteri>
# Sbagliato: Base64 della stringa esadecimale (64 byte ASCII → 88 caratteri Base64 — il doppio)
hex_digest = hashlib.sha256(data).hexdigest()
wrong = base64.b64encode(hex_digest.encode()).decode()
print(f"Lunghezza errata: {len(wrong)} caratteri") # 88 — non quello che si aspettano le API.digest(), non .hexdigest(), prima della codifica Base64.Riferimento hashlib.sha256()
Il costruttore e i metodi di un oggetto hash SHA-256:
Parametri di hmac.new() per l'hashing con chiave:
La libreria cryptography — Un'API SHA-256 alternativa
Il pacchetto cryptography offre un'API diversa per SHA-256 tramite le sue primitive hazmat. Raramente la utilizzo quando ho bisogno solo di un hash — hashlib è più semplice e non ha dipendenze esterne. Ma se il progetto dipende già da cryptography per TLS, X.509 o cifratura simmetrica, usare la sua API per gli hash mantiene tutto sotto un'unica libreria e garantisce una gestione coerente degli errori.
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(b"deployment-v4.2.1") result = digest.finalize() # 32 byte grezzi print(result.hex()) # stringa hex di 64 caratteri # a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
cryptography richiede pip install cryptography. L'oggetto hash è monouso: chiamare .finalize() una seconda volta genera AlreadyFinalized. Usare .copy() prima di finalizzare se si ha bisogno di ramificare lo stato hash.Hash di dati da file e risposta API
Due scenari ricorrono continuamente: calcolare l'hash di un file su disco per verificare un artefatto di release, e calcolare l'hash del corpo di una risposta HTTP per usarlo come chiave di cache o per verificare un webhook.
Leggi file → Calcola SHA-256 → Confronta
import hashlib
import sys
def hash_file_safe(filepath: str) -> str | None:
"""Calcola l'hash di un file con corretta gestione degli errori."""
try:
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(16384), b""):
h.update(chunk)
return h.hexdigest()
except FileNotFoundError:
print(f"Errore: {filepath} non trovato", file=sys.stderr)
return None
except PermissionError:
print(f"Errore: permesso di lettura negato per {filepath}", file=sys.stderr)
return None
result = hash_file_safe("/etc/nginx/nginx.conf")
if result:
print(f"SHA-256: {result}")Risposta HTTP → Hash del corpo come chiave di cache
import hashlib
import urllib.request
import json
def fetch_and_hash(url: str) -> tuple[dict, str]:
"""Recupera JSON da un'API e restituisce sia i dati che il loro hash SHA-256."""
try:
with urllib.request.urlopen(url, timeout=10) as resp:
body = resp.read()
content_hash = hashlib.sha256(body).hexdigest()
data = json.loads(body)
return data, content_hash
except urllib.error.URLError as exc:
raise ConnectionError(f"Impossibile raggiungere {url}: {exc}") from exc
# Chiave di cache basata sul contenuto della risposta
data, digest = fetch_and_hash("https://api.exchange.internal/v2/rates")
print(f"Hash risposta: {digest[:16]}...")
print(f"EUR/USD: {data.get('rates', {}).get('EUR', 'N/A')}")Per un controllo rapido, il generatore SHA-256 di ToolDeck funziona interamente nel browser — nessun codice necessario.
Hashing SHA-256 da riga di comando
A volte serve un hash rapido nel terminale durante un incident o un deploy. Il modulo hashlib di Python non ha un sottocomando CLI integrato (a differenza di python3 -m json.tool), ma si può usare un one-liner o i tool di sistema.
# One-liner Python echo -n "deployment-v4.2.1" | python3 -c "import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())" # macOS / BSD echo -n "deployment-v4.2.1" | shasum -a 256 # Linux (coreutils) echo -n "deployment-v4.2.1" | sha256sum # OpenSSL (multi-piattaforma) echo -n "deployment-v4.2.1" | openssl dgst -sha256
# Hash di un tarball di release sha256sum release-v4.2.1.tar.gz # oppure openssl dgst -sha256 release-v4.2.1.tar.gz # Verifica contro un checksum noto echo "a8f5f167f44f4964e6c998dee827110c release-v4.2.1.tar.gz" | sha256sum -c - # release-v4.2.1.tar.gz: OK
echo -n(senza newline finale) quando si calcola l'hash di stringhe dalla riga di comando. Un semplice echo aggiunge \n, che cambia l'hash. Questa è la causa principale per cui si ottengono hash diversi tra Python e la shell.Alternativa ad alte prestazioni — hashlib con OpenSSL e pycryptodome
Su CPython, hashlib.sha256() delega già all'implementazione C di OpenSSL, quindi è veloce — tipicamente 500+ MB/s su hardware moderno.
Se l'hashing SHA-256 emerge nel profiler — ad esempio si stanno calcolando checksum per migliaia di file in una pipeline CI o si sta calcolando l'hash di ogni corpo di richiesta in un gateway API ad alto traffico — esistono due opzioni: ottimizzare il pattern di chiamata a hashlib, oppure passare a pycryptodome per un'API crittografica unificata che copre anche SHA-3 e BLAKE2:
pip install pycryptodome
from Crypto.Hash import SHA256 h = SHA256.new() h.update(b"deployment-v4.2.1") print(h.hexdigest()) # a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
Per l'hashing parallelo di file ad alto volume, i guadagni maggiori si ottengono riducendo l'overhead Python attraverso blocchi più grandi e il threading:
import hashlib
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
def hash_file(path: Path) -> tuple[str, str]:
"""Calcola l'hash di un singolo file e restituisce (percorso, digest esadecimale)."""
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""): # blocchi da 64 KB
h.update(chunk)
return str(path), h.hexdigest()
def hash_directory(directory: str, pattern: str = "*.tar.gz") -> dict[str, str]:
"""Calcola l'hash di tutti i file corrispondenti in parallelo usando i thread."""
files = list(Path(directory).glob(pattern))
results = {}
with ThreadPoolExecutor(max_workers=os.cpu_count()) as pool:
for path, digest in pool.map(hash_file, files):
results[path] = digest
return results
# Hash di tutti gli artefatti di release in parallelo
checksums = hash_directory("/var/releases", "*.tar.gz")
for path, digest in checksums.items():
print(f"{digest} {path}")Usare blocchi da 64 KB invece di 8 KB riduce di 8 volte il numero di chiamate da Python a C. I thread funzionano bene qui perché il GIL viene rilasciato durante l'hashing a livello C — il collo di bottiglia è l'I/O su disco, non la CPU.
Output nel terminale con evidenziazione della sintassi
La libreria rich è utile quando si vuole verificare un batch di file e si desidera una tabella con lo stato pass/fail per ogni file, anziché un flusso di stringhe esadecimali non elaborate.
pip install rich
import hashlib
from pathlib import Path
from rich.console import Console
from rich.table import Table
console = Console()
def hash_and_display(files: list[str], expected: dict[str, str]) -> None:
"""Calcola l'hash dei file e mostra i risultati con verifica a colori."""
table = Table(title="Verifica SHA-256")
table.add_column("File", style="cyan")
table.add_column("SHA-256", style="dim", max_width=20)
table.add_column("Stato")
for filepath in files:
h = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
digest = h.hexdigest()
name = Path(filepath).name
status = "[green]✓ OK[/green]" if expected.get(name) == digest else "[red]✗ MISMATCH[/red]"
table.add_row(name, f"{digest[:16]}...", status)
console.print(table)
# Utilizzo
expected_checksums = {
"api-gateway-v3.1.tar.gz": "a8f5f167f44f4964...",
"worker-v3.1.tar.gz": "7d3f8c2a1b9e4f5d...",
}
hash_and_display(
["/var/releases/api-gateway-v3.1.tar.gz", "/var/releases/worker-v3.1.tar.gz"],
expected_checksums,
)console.print(data, highlight=False) o reindirizzare su file con Console(file=open(...)).Lavoro con file di grandi dimensioni
Il pattern a blocchi con .update() gestisce file di qualsiasi dimensione con utilizzo costante della memoria. Per file molto grandi (immagini disco multi-GB, backup di database), la preoccupazione principale si sposta dalla memoria al feedback per l'utente — calcolare l'hash di 10 GB a 500 MB/s richiede comunque 20 secondi, e il silenzio durante quel tempo mette a disagio.
import hashlib
import os
def sha256_with_progress(filepath: str) -> str:
"""Calcola l'hash di un file di grandi dimensioni con indicatore di avanzamento su stderr."""
file_size = os.path.getsize(filepath)
h = hashlib.sha256()
bytes_read = 0
with open(filepath, "rb") as f:
while chunk := f.read(1 << 20): # blocchi da 1 MB
h.update(chunk)
bytes_read += len(chunk)
pct = (bytes_read / file_size) * 100
print(f"\r Hashing: {pct:.1f}% ({bytes_read >> 20} MB / {file_size >> 20} MB)",
end="", flush=True)
print() # nuova riga dopo il progresso
return h.hexdigest()
digest = sha256_with_progress("/mnt/backups/db-snapshot-2026-03.sql.gz")
print(f"SHA-256: {digest}")NDJSON / JSON Lines — Hash di ogni record separatamente
import hashlib
import json
def hash_ndjson_records(filepath: str) -> dict[str, str]:
"""Calcola l'hash di ogni record JSON in un file NDJSON per la deduplicazione."""
seen = {}
with open(filepath, "r", encoding="utf-8") as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
if not line:
continue
try:
record = json.loads(line)
# Normalizza prima dell'hashing: ordina le chiavi per output deterministico
canonical = json.dumps(record, sort_keys=True, separators=(",", ":"))
digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
if digest in seen:
print(f"Riga {line_num}: duplicato della riga {seen[digest]}")
else:
seen[digest] = line_num
except json.JSONDecodeError:
print(f"Riga {line_num}: JSON non valido, saltata")
print(f"Elaborate {line_num} righe, {len(seen)} record unici")
return seen
hash_ndjson_records("telemetry-events-2026-03.ndjson")hashlib.sha256(data) al ciclo a blocchi con .update()quando i file superano i 50–100 MB. Al di sotto di quella soglia, leggere l'intero file con f.read() va bene — l'utilizzo della memoria sarà all'incirca pari alla dimensione del file.Errori comuni
Problema: hashlib.sha256('testo') genera TypeError: Unicode-objects must be encoded before hashing. La funzione richiede bytes, non str.
Soluzione: Codificare prima la stringa: hashlib.sha256('testo'.encode('utf-8')). Oppure usare un letterale b'' per valori fissi nel codice.
import hashlib
digest = hashlib.sha256("deployment-v4.2.1").hexdigest()
# TypeError: Unicode-objects must be encoded before hashingimport hashlib
digest = hashlib.sha256("deployment-v4.2.1".encode("utf-8")).hexdigest()
# Funziona — restituisce una stringa hex di 64 caratteriProblema: L'operatore == si interrompe al primo byte diverso. Un attaccante può misurare i tempi di risposta per indovinare la firma corretta byte per byte.
Soluzione: Usare hmac.compare_digest() per tutti i confronti sensibili alla sicurezza — viene eseguito in tempo costante indipendentemente da dove si verifica la discrepanza.
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if received_sig == expected_sig: # vulnerabile ad attacchi temporali
process_webhook(body)received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if hmac.compare_digest(received_sig, expected_sig): # tempo costante
process_webhook(body)Problema: base64.b64encode(digest.hexdigest().encode()) produce una stringa di 88 caratteri — il doppio dei 44 attesi. Le API che si aspettano SHA-256 codificato in Base64 la rifiuteranno.
Soluzione: Chiamare .digest() (byte grezzi) prima della codifica Base64, non .hexdigest() (stringa esadecimale).
import hashlib, base64 hex_str = hashlib.sha256(data).hexdigest() b64 = base64.b64encode(hex_str.encode()) # 88 caratteri — sbagliato!
import hashlib, base64 raw = hashlib.sha256(data).digest() b64 = base64.b64encode(raw) # 44 caratteri — corretto
Problema: hashlib.sha256(open('large.iso', 'rb').read()) carica l'intero file in memoria. Un file da 4 GB richiede 4 GB di RAM solo per il calcolo dell'hash.
Soluzione: Leggere a blocchi con un ciclo e .update(). L'utilizzo della memoria rimane costante indipendentemente dalla dimensione del file.
import hashlib
# Carica l'intero file da 4 GB in memoria
digest = hashlib.sha256(open("disk.iso", "rb").read()).hexdigest()import hashlib
h = hashlib.sha256()
with open("disk.iso", "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
digest = h.hexdigest() # utilizzo costante della memoriahashlib vs hmac vs alternative — Confronto rapido
Per l'hashing diretto — checksum, chiavi di cache, fingerprinting del contenuto — usare hashlib.sha256(). Passare a hmac.new() non appena si ha bisogno di una chiave segreta (webhook, firme API, autenticazione token). Ricorrere alla libreria cryptography solo se il progetto la usa già per cifratura o TLS — aggiungere una dipendenza da un'estensione C solo per l'hashing è eccessivo quando hashlib è già basato su OpenSSL.
Si può decifrare SHA-256? — Hashing vs cifratura
Risposta breve: no. SHA-256 è una funzione unidirezionale. L'algoritmo è progettato per essere irreversibile — non è possibile ricostruire l'input originale dal digest di 256 bit. Questa non è una limitazione implementativa; è una proprietà matematica della funzione hash. Lo spazio di output di 256 bit è astronomicamente grande (2256 valori possibili), e la funzione scarta informazioni durante i suoi 64 round di compressione.
Gli attaccanti possono tentare attacchi brute-force o dizionario contro input deboli (password comuni, stringhe brevi), ma per qualsiasi input con buona entropia — chiavi API, token casuali, contenuti di file — invertire SHA-256 è computazionalmente impraticabile con l'hardware attuale. Se si ha bisogno di una trasformazione reversibile, usare la cifratura simmetrica:
# Hashing — unidirezionale, non recuperabile import hashlib digest = hashlib.sha256(b"secret-config-value").hexdigest() # Impossibile risalire a "secret-config-value" dal digest # Cifratura — bidirezionale, decifrabile con la chiave from cryptography.fernet import Fernet key = Fernet.generate_key() cipher = Fernet(key) encrypted = cipher.encrypt(b"secret-config-value") decrypted = cipher.decrypt(encrypted) print(decrypted) # b"secret-config-value" — originale recuperato
Per generare rapidamente un hash SHA-256 senza installare nulla, lo strumento online funziona interamente nel browser.
Come verificare se una stringa è un hash SHA-256 valido in Python
Un digest SHA-256 esadecimale valido è esattamente 64 caratteri esadecimali (0-9, a-f, A-F). Una rapida validazione prima di elaborare input non affidabili previene errori a valle difficili da interpretare.
import re
def is_sha256_hex(value: str) -> bool:
"""Verifica se una stringa corrisponde al formato digest SHA-256 esadecimale."""
return bool(re.fullmatch(r"[a-fA-F0-9]{64}", value))
# Casi di test
print(is_sha256_hex("e3b0c44298fc1c149afbf4c8996fb924"
"27ae41e4649b934ca495991b7852b855")) # True — SHA-256 della stringa vuota
print(is_sha256_hex("e3b0c44298fc1c14")) # False — troppo corto
print(is_sha256_hex("zzzz" * 16)) # False — caratteri hex non validiDomande frequenti
Come si calcola l'hash SHA-256 di una stringa in Python?
Si chiama hashlib.sha256() con la stringa codificata in bytes. Le stringhe Python sono Unicode e le funzioni hash operano su byte grezzi, quindi è necessario chiamare .encode("utf-8") prima. Il metodo .hexdigest() restituisce la familiare stringa esadecimale di 64 caratteri.
import hashlib
api_key = "sk_live_9f3a7b2e1d4c"
digest = hashlib.sha256(api_key.encode("utf-8")).hexdigest()
print(digest)
# e3b7c4a1f8d2...64 caratteri esadecimaliÈ possibile decifrare un hash SHA-256 per ottenere il testo originale?
No. SHA-256 è una funzione unidirezionale — trasforma un input di lunghezza arbitraria in un output fisso di 256 bit, scartando la struttura nel processo. Non esiste inversa matematica. Gli attaccanti possono tentare attacchi brute-force o rainbow table contro input deboli (password corte, parole comuni), ma per qualsiasi input con entropia ragionevole, invertire SHA-256 è computazionalmente impraticabile. Se si ha bisogno di una trasformazione reversibile, usare la cifratura (AES-GCM, Fernet) invece dell'hashing.
Qual è la differenza tra .digest() e .hexdigest()?
.digest() restituisce i 32 byte grezzi dell'hash come oggetto bytes. .hexdigest() restituisce gli stessi dati codificati come stringa esadecimale minuscola di 64 caratteri. Usare .digest() quando si ha bisogno di output binario — input per HMAC, codifica Base64 o scrittura su protocolli binari. Usare .hexdigest() quando si ha bisogno di una stringa leggibile per log, archiviazione in database o confronto di checksum.
import hashlib h = hashlib.sha256(b"deployment-v4.2.1") print(len(h.digest())) # 32 (byte) print(len(h.hexdigest())) # 64 (caratteri esadecimali)
Come si calcola il checksum SHA-256 di un file in Python?
Si apre il file in modalità binaria e lo si alimenta all'hasher a blocchi con .update(). Su Python 3.11+, si può usare hashlib.file_digest() per un'API ancora più semplice. Non chiamare mai f.read() su file di grandi dimensioni — carica l'intero file in memoria.
import hashlib
def sha256_file(path: str) -> str:
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
print(sha256_file("release-v4.2.1.tar.gz"))Come si crea una firma HMAC-SHA256 in Python?
Si usa il modulo hmac con hashlib.sha256 come digestmod. Si passano la chiave segreta e il messaggio come bytes. Il risultato è un hash con chiave che garantisce sia l'integrità che l'autenticità — il ricevitore deve avere la stessa chiave per verificare.
import hmac
import hashlib
secret = b"webhook_secret_9f3a"
payload = b'{"event":"payment.completed","amount":4999}'
signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
print(signature) # HMAC esadecimale di 64 caratteriCome si verifica se una stringa è un digest SHA-256 esadecimale valido?
Un digest SHA-256 esadecimale è esattamente 64 caratteri esadecimali. Si usa una regex o un semplice controllo di lunghezza e caratteri. L'approccio con regex è il più leggibile.
import re
def is_sha256(s: str) -> bool:
return bool(re.fullmatch(r"[a-fA-F0-9]{64}", s))
print(is_sha256("e3b0c44298fc1c149afbf4c8996fb924"
"27ae41e4649b934ca495991b7852b855")) # True
print(is_sha256("not-a-hash")) # FalseStrumenti correlati
Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.
Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.