Rilevamento delle manomissioni negli archivi documentali: come funzionano le catene di hash
Non è possibile verificare che un documento non sia stato manomesso semplicemente guardandolo. Un PDF modificato appare identico all’originale. Un file sostituito ha lo stesso nome. Una voce di audit log modificata silenziosamente non lascia tracce visibili.
Il rilevamento delle manomissioni richiede un meccanismo che renda la modifica un artefatto rilevabile, idealmente uno che possa essere verificato da qualcuno che non era presente alla creazione originale. Le catene di hash sono uno dei meccanismi principali che raggiungono questo obiettivo.
Cosa fa un hash crittografico
Una funzione hash crittografica prende un input di lunghezza arbitraria e produce un output di lunghezza fissa (il digest) con due proprietà critiche:
- Determinismo: lo stesso input produce sempre lo stesso output
- Effetto valanga: qualsiasi modifica all’input, anche un singolo bit, produce un output completamente diverso
SHA-256 produce un digest di 256 bit (32 byte). Per qualsiasi input pratico, è computazionalmente impossibile:
- Trovare due input diversi che producono lo stesso output (resistenza alle collisioni)
- Ricostruire l’input dall’output (resistenza alle preimmagini)
- Produrre un output specifico senza conoscere l’input (resistenza alle seconde preimmagini)
Queste proprietà fanno sì che un hash SHA-256 sia un’impronta digitale affidabile di un documento. Se l’hash corrisponde, il documento è identico a quello che è stato sottoposto all’hashing. Se l’hash non corrisponde, qualcosa è cambiato.
var hash = SHA256.HashData(File.ReadAllBytes("invoice.pdf"));
Console.WriteLine(Convert.ToHexString(hash));
// A3F8C2...1B7D (64 hex chars, always the same for this file, always different if file changes)
Perché un singolo hash non è sufficiente
L’hashing di un documento fornisce un’impronta digitale in un momento specifico. Ma chi conserva quella impronta digitale e cosa impedisce a qualcuno di sostituire sia il documento che l’hash memorizzato?
Se si memorizza l’hash nello stesso sistema del documento e si controlla quel sistema, un attaccante determinato (o un soggetto interno che copre le proprie tracce) può sostituire entrambi. L’hash corrisponde ancora al nuovo documento. La manomissione è non rilevabile.
Per questo motivo il rilevamento delle manomissioni richiede un ancoraggio esterno: un hash memorizzato in un posto che il custode non può modificare unilateralmente. Un timestamp RFC 3161 è il meccanismo standard: incorpora l’hash del documento all’interno di un token firmato da una TSA terza. Il custode non può alterare il token senza la chiave privata della TSA.
Cosa aggiunge una catena di hash
Una catena di hash estende questo concetto a una sequenza di eventi. Invece di eseguire l’hashing solo del documento, si esegue l’hashing di ogni voce di audit e si include l’hash della voce precedente in quella corrente.
Hash(Entry N) = SHA256( EventData(N) + Hash(Entry N-1) )
Questo crea una catena in cui ogni voce si impegna a tutte le voci precedenti. Se si cambia la voce 3 in una catena di 10 voci, Hash(voce 3) cambia. Poiché la voce 4 include Hash(voce 3), Hash(voce 4) cambia. E così via fino alla voce 10.
Non è possibile modificare silenziosamente una voce passata. La catena non verrà validata.
Ecco un’implementazione minimale:
public sealed record AuditEntry(
string EventType,
string Actor,
DateTimeOffset Timestamp,
string DocumentHash,
string PreviousEntryHash)
{
public string ComputeHash()
{
var raw = $"{EventType}|{Actor}|{Timestamp:O}|{DocumentHash}|{PreviousEntryHash}";
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(raw)));
}
}
public static string AppendEntry(IList<AuditEntry> chain, string eventType,
string actor, byte[] documentBytes)
{
var previousHash = chain.Count == 0
? new string('0', 64)
: chain[^1].ComputeHash();
var docHash = Convert.ToHexString(SHA256.HashData(documentBytes));
var entry = new AuditEntry(eventType, actor, DateTimeOffset.UtcNow, docHash, previousHash);
chain.Add(entry);
return entry.ComputeHash();
}
public static bool VerifyChain(IList<AuditEntry> chain)
{
var expectedPrevious = new string('0', 64);
foreach (var entry in chain)
{
if (entry.PreviousEntryHash != expectedPrevious) return false;
expectedPrevious = entry.ComputeHash();
}
return true;
}
La verifica è O(n) e non richiede chiamate esterne. Chiunque disponga della catena può eseguire VerifyChain e confermare che nessuna voce è stata modificata.
Ancorare la catena con un timestamp
Una catena di hash verificata dimostra la coerenza interna: nessuna voce è stata modificata dopo la costruzione della catena. Ma non dimostra quando la catena è stata costruita.
Se si costruisce oggi una catena fraudolenta e si sostiene che rappresenti eventi di tre anni fa, la catena stessa verrà verificata correttamente. La catena di hash non ha un orologio.
Qui i timestamp RFC 3161 ancorano la catena al tempo reale. In punti chiave della catena (come minimo: quando la catena è finalizzata; idealmente a ogni evento significativo), si richiede un timestamp a una TSA qualificata. La TSA incorpora l’hash della voce corrente in un token firmato con un timestamp attendibile.
Ora la catena ha un ancoraggio esterno. Non è possibile retrodatare la catena oltre il timestamp più vecchio, perché il token della TSA dimostra che l’hash esisteva in quel momento nel tempo, e l’orologio della TSA è autorevole.
// After appending an entry, request a timestamp for the entry hash
var entryHash = AppendEntry(chain, "created", "api:tenant-1", documentBytes);
var entryHashBytes = Convert.FromHexString(entryHash);
var timestampToken = await RequestTimestampAsync(entryHashBytes, tsaUrl);
// Store the token alongside the chain entry
chain[^1] = chain[^1] with { TimestampToken = timestampToken };
Un revisore che verifica la catena può:
- Verificare che la catena di hash sia internamente coerente (nessuna modifica)
- Verificare ogni token di timestamp rispetto al certificato pubblico della TSA (nessuna retrodatazione)
- Verificare l’hash del documento in ogni voce rispetto al documento corrente (nessuna sostituzione)
Catene di hash vs. altri meccanismi di integrità
Log solo-append (ad esempio, tabelle di database senza DELETE): Impediscono l’eliminazione tramite controlli di accesso, ma sono affidabili solo quanto il sistema di controllo degli accessi. Un amministratore di database può aggirarli. Le catene di hash sono applicate matematicamente, non tramite criteri.
Firme digitali su singoli documenti: Una firma dimostra che un documento è stato firmato da una chiave specifica al momento della firma. Non registra cosa è accaduto al documento in seguito. La catena di custodia richiede la registrazione degli eventi successivi alla firma (trasmissione, conversione, archiviazione), non solo il momento della firma.
Blockchain: Utilizza catene di hash come primitiva fondamentale, ma aggiunge il consenso distribuito per impedire a qualsiasi singola parte di riscrivere la storia. Per gli archivi documentali, una catena di hash locale ancorata da timestamp RFC 3161 esterni fornisce la stessa garanzia di resistenza alle manomissioni senza la complessità di una rete distribuita. La distinzione è importante: non è necessario il consenso tra estranei per dimostrare che un documento non è stato modificato; è necessario un ancoraggio esterno che non si controlla.
Storage WORM (Write Once Read Many): Impedisce la modifica a livello di storage. Il mezzo fisicamente non può essere sovrascritto. Questo è complementare alle catene di hash, non un sostituto. WORM dimostra che i bit memorizzati non sono cambiati. Una catena di hash dimostra quale sequenza di eventi ha prodotto quei bit. Entrambi insieme forniscono garanzie più solide di ciascuno da solo.
Come appare la verifica nella pratica
Un flusso di lavoro di verifica completo per un documento in un archivio con catena di hash:
- Recuperare il documento e la traccia di audit dall’archivio
- Calcolare SHA-256 del documento; confermare che corrisponde al
documentHashnell’ultima voce della catena - Percorrere la catena a ritroso, ricalcolando ogni
entryHashe confermando che corrisponde alpreviousEntryHashnella voce successiva - Per ogni voce con un token di timestamp, verificare il token rispetto al certificato della TSA e confermare che l’hash nel token corrisponde all’
entryHashcalcolato - Confermare che l’hash del documento nel passaggio 2 corrisponda all’hash del documento nella prima voce che copre la forma definitiva del documento
Se tutti e cinque i passaggi vengono superati, il documento è verificato come non modificato dalla sua creazione, la traccia di audit registra accuratamente ogni fase di elaborazione e i timestamp forniscono ancoraggi temporali verificabili esternamente.
Questa verifica richiede: il documento, la traccia di audit, i token di timestamp e i certificati pubblici della TSA. Nessun accesso alla rete. Nessuna dipendenza dall’operatività dei sistemi del custode. La prova è autosufficiente.
Il rilevamento delle manomissioni come infrastruttura
La maggior parte delle organizzazioni non implementa catene di hash perché sembrano una funzionalità avanzata. Non lo sono. SHA-256 è disponibile in ogni runtime moderno. La costruzione della catena è una dozzina di righe di codice. Il vantaggio è che si può dimostrare l’integrità del documento a una terza parte scettica senza fare affidamento sulla sua fiducia nell’organizzazione.
Per i settori regolamentati (finanza, sanità, assicurazioni, governo), quella prova non è facoltativa. Per qualsiasi organizzazione che voglia presentare documenti digitali come prove in una controversia, è il livello base.
SealDoc implementa tracce di audit con catena di hash per ogni documento nella sua pipeline. La catena viene esportata nel Legal Evidence Pack ed è verificabile offline. Per le organizzazioni che devono costruire la propria infrastruttura di prove, l’API SealDoc espone la traccia di audit e i token di timestamp come dati strutturati che è possibile incorporare nei propri flussi di lavoro di verifica.