
Nell’era digitale, la gestione sicura e affidabile dei documenti è una sfida cruciale. Assicurare che un documento non sia stato alterato e che le firme apposte siano autentiche e in una sequenza specifica è fondamentale per processi legali, finanziari e amministrativi.
L’obiettivo dell’articolo si propone di illustrare la Signature Chain come soluzione per la sicurezza dei documenti digitali, garantendo l’inalterabilità perpetua delle firme, l’integrità del contenuto e la rigorosa sequenzialità delle apposizioni. Tale metodologia conferisce un elevato grado di affidabilità, assicurando l’inviolabilità del documento e l’immodificabilità della sua cronistoria. Per una comprensione pratica, l’articolo include anche un esempio dimostrativo tramite una Proof of Concept (PoC).
- Cos'è una Signature Chain
- Architettura e funzionamento
- Esempio di record nella tabella
- Schema della catena delle firme
- Meccanismi di Sicurezza in PostgreSQL per la Signature Chain
- Verifica dell'integrità del documento originale (considerazioni esterne)
- Caratteristiche chiave della catena
- Come avviare la PoC
- Output atteso (estratto)
- Appendice: considerazioni sugli algoritmi crittografici
- Conclusioni
Cos’è una Signature Chain
La Signature Chain è un meccanismo crittografico che crea una sequenza immutabile di firme digitali su un documento, garantendone autenticità, integrità e sequenzialità. Questo concetto è stato dimostrato in questa Proof of Concept (POC) che utilizza PostgreSQL e Python, impiegando chiavi RSA generate in memoria per illustrare il funzionamento. Per un ambiente di produzione reale, si raccomanda l’uso di Hardware Security Modules (HSM) o i key vault (sistemi di custodia certificati per chiavi crittografiche), una Timestamp Authority (TSA) per la certificazione temporale esatta (assicurando l’esatta data e ora di ogni sottoscrizione, fino al millisecondo, eliminando contestazioni temporali), e certificati digitali qualificati, rilasciati da enti certificatori riconosciuti. Pertanto, si sottolinea la natura non sperimentale ma robusta e legalmente valida di tale sistema per applicazioni reali.
Architettura e funzionamento
Il sistema di Signature Chain, implementato dalla PoC, si basa su un’applicazione realizzata in Python che interagisce con un database per la memorizzazione e la verifica delle firme. Ogni firma apposta su un documento viene registrata come un “blocco” in una tabella del database, ad esempio signature_chain
. Tale interazione è assimilabile all’operato di un segretario altamente preciso e incorruttibile, il quale registra ogni singola sottoscrizione con meticolosità, senza omissioni o possibilità di corruzione.
Il diagramma a seguire illustra l’architettura del sistema implementata da questa PoC.

La struttura di ogni blocco è progettata per garantire l’integrità e la tracciabilità. Ecco il design di ogni blocco che corrisponde al modello della tabella signature_chain
.
id
(SERIAL PRIMARY KEY): Un identificativo progressivo unico per il blocco.document_id
(UUID): Un identificativo UUID che lega la catena di firme a uno specifico documento. Questo permette di avere catene separate per documenti diversi.signer
(VARCHAR(255)): Il nome o l’identificativo del firmatario.document_hash
(VARCHAR(64)): L’hash SHA-256 del contenuto originale del documento. Questo hash rimane costante per tutte le firme all’interno della stessa catena e dello stesso documento.prev_hash
(VARCHAR(128), NULLABLE): L’hash della firma (signature
) del blocco precedente nella catena. Per il primo blocco, noto come “blocco genesi”, questo valore èNULL
.chain_hash
(TEXT, GENERATED ALWAYS AS STORED): Colonna Calcolata Automaticamente. Questa colonna contiene l’hash SHA-256 della concatenazione diprev_hash
(o una stringa vuota seprev_hash
èNULL
) edocument_hash
del blocco corrente. In pratica, rappresenta l’hash dei dati che vengono effettivamente firmati digitalmente per produrre il camposignature
. Viene calcolata e memorizzata automaticamente dal database (encode(digest(coalesce(prev_hash, '') || document_hash, 'sha256'), 'hex')
). Sebbene non sia utilizzata attivamente dallo script Python per la logica di costruzione o verifica della catena (che ricalcola questi dati in memoria), può essere utile per query dirette al database, per debug, o per meccanismi di verifica alternativi a livello di database.signature
(VARCHAR(128)): La firma digitale RSA vera e propria. Viene calcolata firmando la concatenazione diprev_hash
edocument_hash
del blocco corrente. Questo campo funge da hash crittografico del blocco corrente e diventa ilprev_hash
per il blocco successivo.created_at
(TIMESTAMP DEFAULT CURRENT_TIMESTAMP): Il timestamp di creazione del blocco.
Esempio di record nella tabella
Di seguito è presentata un’illustrazione dei record all’interno della tabella signature_chain
, con esempi concreti che dimostrano la correlazione tra i campi e la formazione della catena:
Blocco 1 (Genesi – Firmatario: Antonio)
Campo | Valore | Descrizione |
id | 1 | L’identificativo del blocco, il primo in questa catena. Un semplice contatore, ma essenziale per l’ordinamento. |
document_id | c4a7a134-8a02-4bad-bc9f-395f7f0f1a33 | L’identificativo universale univoco del documento oggetto di firma. Questo UUID (Universally Unique Identifier) assicura la distinzione di questo documento da qualsiasi altro nel sistema. |
signer | Antonio | L’identità del primo firmatario, chiaramente registrata. |
document_hash | f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2 | L’hash SHA-256 del documento originale. Questo valore rappresenta il “DNA” crittografico del documento, una stringa di 64 caratteri esadecimali che lo identifica in modo univoco. Qualsiasi modifica al documento comporterebbe un hash differente. |
prev_hash | NULL | Poiché si tratta del primo blocco, non esiste alcuna firma precedente a cui collegarsi, pertanto il valore è nullo. Ciò segnala l’inizio della catena. |
signature | a3f5b1…c72e | Firma RSA: concatenazione di una stringa vuota (prev_hash nullo) e hash documento, firmata da Antonio. Risultato: stringa esadecimale di 128 caratteri. |
created_at | 2025-05-21 14:30:00.123456+00 | La data e l’ora precise di apposizione della firma da parte di Antonio, inclusi i microsecondi e il fuso orario UTC. Questo timestamp è cruciale per la cronologia. |
Blocco 2 (Firmatario: Marianna)
Campo | Valore | Descrizione |
id | 2 | L’identificativo del blocco, il secondo in questa catena. |
document_id | c4a7a134-8a02-4bad-bc9f-395f7f0f1a33 | L’identificativo del documento rimane invariato, poiché la firma si riferisce al medesimo file. Questo è fondamentale per raggruppare tutte le firme relative a un unico documento. |
signer | Marianna | L’identità del firmatario di questo blocco. |
document_hash | f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2 | L’hash del documento rimane lo stesso, a riprova che il documento non ha subito modifiche tra la firma di Antonio e quella di Marianna. Una discrepanza in questo hash invaliderebbe la catena. |
prev_hash | a3f5b1…c72e | Questo valore corrisponde alla signature di Antonio dal Blocco 1. Costituisce il legame crittografico al blocco precedente, garantendo la sequenzialità. Questo è l’elemento chiave della concatenazione. |
signature | b8e0d9…f4a1 | Questa firma, calcolata sulla concatenazione della signature di Antonio e l’hash del documento, è unica per questo blocco e sequenza. |
created_at | 2025-05-21 14:35:00.654321+00 | La data e l’ora precise di apposizione della firma da parte di Marianna. Si noti che è successiva a quella di Antonio, confermando l’ordine cronologico. |
Schema della catena delle firme
Il diagramma seguente illustra come i blocchi sono concatenati:

Questo schema garantisce che ogni blocco sia inscindibilmente legato al precedente, formando una catena di integrità.
Meccanismi di Sicurezza in PostgreSQL per la Signature Chain
PostgreSQL, come database relazionale robusto, offre diversi meccanismi di sicurezza che sono fondamentali per implementare e mantenere l’integrità e l’immutabilità dei dati in una Signature Chain. L’esempio fornito nello script init.sql
illustra l’applicazione di alcuni di questi principi chiave:
1. Principio del Privilegio Minimo (Least Privilege Principle)
Questo principio di sicurezza fondamentale suggerisce che a un utente o a un’applicazione dovrebbero essere concessi solo i privilegi minimi necessari per svolgere le proprie funzioni. Nello script init.sql
, questo è implementato attraverso:
Creazione di un utente applicativo dedicato:
<code>CREATE ROLE app_user WITH LOGIN PASSWORD 'app_password';</code>
Code language: SQL (Structured Query Language) (sql)
Viene creato un ruolo specifico (app_user
) per l’applicazione, separato dall’utente amministratore del database.
Concessione di privilegi limitati:
<code>GRANT CONNECT ON DATABASE signature_demo TO app_user;GRANT USAGE ON SCHEMA public TO app_user;GRANT SELECT, INSERT ON signature_chain TO app_user;GRANT USAGE ON SEQUENCE signature_chain_id_seq TO app_user;</code>
Code language: SQL (Structured Query Language) (sql)
A app_user
vengono concessi solo i permessi di connessione al database, uso dello schema pubblico, e specificamente SELECT
e INSERT
sulla tabella signature_chain
, oltre all’uso della sequenza ID. Notare l’assenza di permessi di UPDATE
o DELETE
, che è cruciale per l’immutabilità.
2. Sicurezza a Livello di Riga (Row Level Security – RLS)
La RLS in PostgreSQL permette di definire policy di sicurezza che controllano quali righe possono essere visualizzate o modificate da specifici utenti o ruoli, anche se questi utenti hanno permessi a livello di tabella. Questo aggiunge un ulteriore strato di controllo granulare. Nello script:
Abilitazione e forzatura di RLS:
ALTER TABLE signature_chain ENABLE ROW LEVEL SECURITY;
ALTER TABLE signature_chain FORCE ROW LEVEL SECURITY;
Code language: SQL (Structured Query Language) (sql)
Questi comandi attivano la RLS per la tabella signature_chain
e la forzano anche per il proprietario della tabella, garantendo che le policy siano sempre applicate.
Definizione delle policy di immutabilità:
CREATE POLICY allow_inserts_for_public ON signature_chain
FOR INSERT TO PUBLIC WITH CHECK (true);
Code language: PHP (php)
Questa policy permette le operazioni di inserimento, essenziali per aggiungere nuove firme alla catena.
CREATE POLICY allow_select_for_public ON signature_chain
FOR SELECT TO PUBLIC USING (true);
Code language: PHP (php)
Questa policy consente la lettura di tutte le righe, necessaria per la verifica della catena.
CREATE POLICY no_updates_for_public ON signature_chain
FOR UPDATE TO PUBLIC USING (false);
Code language: PHP (php)
Questa policy impedisce esplicitamente qualsiasi operazione di aggiornamento sulla tabella per il ruolo PUBLIC
(che include app_user
).
CREATE POLICY no_deletes_for_public ON signature_chain
FOR DELETE TO PUBLIC USING (false);
Code language: PHP (php)
Analogamente, questa policy impedisce qualsiasi operazione di cancellazione.
3. Revoca Esplicita dei Permessi
Come buona pratica di sicurezza, lo script include anche una revoca esplicita dei permessi di UPDATE
e DELETE
per PUBLIC
:
REVOKE UPDATE, DELETE ON signature_chain FROM PUBLIC;
Code language: PHP (php)
Questa revoca rafforza ulteriormente le policy RLS, assicurando che nessun utente senza permessi specifici possa modificare o eliminare i dati della catena, anche se per qualche motivo le policy RLS non fossero attive o fossero mal configurate.
Combinando il principio del privilegio minimo con la robustezza della Sicurezza a Livello di Riga, PostgreSQL fornisce un ambiente in cui l’immutabilità della Signature Chain è fortemente rafforzata, rendendo estremamente difficile la manomissione dei record una volta che sono stati inseriti. Questo è cruciale per la fiducia e la validità a lungo termine dei documenti firmati digitalmente.
Verifica dell’integrità del documento originale (considerazioni esterne)
Oltre a garantire l’integrità interna della catena di firme, è fondamentale verificare anche l’integrità del documento originale conservato esternamente. La Signature Chain valida le firme apposte su un documento con un document_hash
specifico, ma non rileva modifiche al file sorgente nel suo repository esterno.
Per una verifica completa, il processo dovrebbe includere passaggi aggiuntivi:
- Recupero del Documento Attuale: utilizzando il
document_id
presente nellasignature_chain
, recuperare la versione corrente del documento dal suo sistema di archiviazione primario. - Calcolo dell’Hash Attuale: calcolare l’hash SHA-256 del documento recuperato al punto precedente.
- Confronto Fondamentale: confrontare questo hash appena calcolato con il
document_hash
memorizzato nel primo blocco (o in qualsiasi blocco, dato che dovrebbe essere identico per tutti i blocchi relativi allo stessodocument_id
) dellasignature_chain
.
Se questi due hash non corrispondono, significa che il documento originale è stato alterato o sostituito dopo l’inizio del processo di firma. L’implementazione di questa verifica richiede un’integrazione con il sistema di gestione documentale.
Caratteristiche chiave della catena
Le proprietà fondamentali che rendono la Signature Chain una tecnologia preziosa sono:
- Immutabilità: una volta aggiunto, un blocco non può essere modificato senza invalidare tutte le firme successive nella catena.
- Integrità del Documento: il
document_hash
assicura che tutte le firme si riferiscano esattamente alla stessa versione del documento. - Sequenzialità Verificabile: il campo
prev_hash
stabilisce un legame cronologico inconfutabile tra le firme, garantendo l’ordine di apposizione. - Autenticità del Firmatario: ogni
signature
è generata con la chiave privata del firmatario, permettendo la verifica dell’autenticità tramite la sua chiave pubblica.
Come avviare la PoC
Per avviare la Proof of Concept e sperimentare la Signature Chain, segui questi passaggi:
1. Requisiti
Assicurati di avere installato:
- Podman (o Docker, con lievi modifiche ai comandi
podman-compose
) - Python 3.9+
- Librerie Python:
psycopg2-binary
,cryptography
.
Installa le dipendenze Python:
pip install -r requirements.txt
Code language: CSS (css)
(Assicurati che requirements.txt
contenga psycopg2-binary
e cryptography
)
2. Configurazione variabili d’ambiente (opzionale)
Lo script main.py
può essere configurato tramite variabili d’ambiente per i dettagli di connessione al database. Se non impostate, saranno usati valori di default.
Esempio d’impostazione delle variabili di ambiente
export APP_DB_USER="mio_user_app"
export APP_DB_PASSWORD="mia_password_app"
export SUPER_DB_USER="postgres_admin"
export SUPER_DB_PASSWORD="admin_password"
export DB_NAME="poc_signatures"
export DB_HOST="localhost"
Code language: JavaScript (javascript)
I valori di default sono rispettivamente: app_user
, app_password
, postgres
, postgres
, signature_demo
, localhost
.
3. Avvio del database PostgreSQL
Viene fornito un file podman-compose.yml
per avviare un’istanza di PostgreSQL con gli utenti e il database necessari pre-configurati. Esegui il seguente comando dalla directory del progetto:
podman-compose -f podman-compose.yml up -d
Code language: CSS (css)
Il comando precedente:
- Avvia un container PostgreSQL.
- Crea il database specificato (default:
signature_demo
). - Crea gli utenti
app_user
(con permessi limitati) epostgres
(superutente). - Esegue lo script
init.sql
per creare la tabellasignature_chain
e impostare i permessi e la Row-Level Security (RLS) perapp_user
. - Espone la porta
5432
del database sull’host.
4. Esegui lo script di firma e verifica
Lo script main.py
simula i seguenti scenari:
- Scenario Utente Applicativo (
app_user
):- Connessione al DB come
app_user
. - Inserimento di una sequenza di firme da parte di diversi firmatari (Antonio, Marianna, Claudio) sullo stesso documento.
- Verifica dell’integrità della catena.
- Tentativo (fallito, grazie a RLS e GRANT) di manomettere un record esistente da parte di
app_user
. - Nuova verifica della catena.
- Connessione al DB come
- Scenario Utente Privilegiato (
super_db_user
, default:postgres
):- Pulizia della tabella.
- Connessione al DB come utente con privilegi elevati.
- Reinserimento della sequenza di firme.
- Verifica dell’integrità della catena.
- Tentativo (riuscito) di manomettere un record esistente da parte del superutente.
- Nuova verifica della catena, che dovrebbe fallire, evidenziando la manomissione.
Per verificare gli scenari sopra descritti, esegui lo script con il comando python main.py
5. Verifica della Catena
La funzione verify_chain
esegue il processo di verifica come illustrato nel seguente diagramma di sequenza:

In sintesi:
- Recupera tutti i record dalla tabella
signature_chain
in ordine di ID. - Per ogni record:
- Controlla che
prev_hash
corrisponda allasignature
del record precedente (a meno che non sia il blocco genesi, doveprev_hash
deve essereNULL
). - Ricostruisce i dati che sono stati originariamente firmati:
(prev_hash || document_hash)
. - Verifica la
signature
del record corrente usando i dati ricostruiti e la chiave pubblica delsigner
associato a quel record.
- Controlla che
- Se una qualsiasi di queste verifiche fallisce, l’intera catena è considerata compromessa.
Output atteso (estratto)
L’output sarà colorato e includerà emoji per indicare lo stato delle operazioni. Un esempio parziale:
💾 Tentativo di pulire la tabella signature_chain come utente 'postgres'...
✅ Tabella signature_chain pulita con successo.
===== SCENARIO 1: Utente Applicativo (app_user) =====
💾 Tentativo di connessione al database 'signature_demo' come utente 'app_user'...
✅ Connessione come app_user riuscita.
🔗==== Sequenza Firme Inserite nella Catena ====
ℹ️ Documento Originale: "Contenuto documento firmato da più persone"
ℹ️ Hash Documento Originale: <hash_del_documento>
----------------------------------------------------------------------
🧱 ID Blocco: 1
Firmatario: Antonio
Hash Documento Firmato: <hash_del_documento>
Hash Catena Precedente: N/A (Blocco Genesi)
Hash Catena Corrente (Firma del Blocco): <signature_blocco_1>...
----------------------------------------------------------------------
🧱 ID Blocco: 2
Firmatario: Marianna
Hash Documento Firmato: <hash_del_documento>
Hash Catena Precedente: <signature_blocco_1>
Hash Catena Corrente (Firma del Blocco): <signature_blocco_2>...
----------------------------------------------------------------------
... (altre firme e output di verifica) ...
🔗==== Verifica Integrità Catena Firme (Contesto: app_user - Post Inserimento) ====
...
✅ La catena delle firme è VALIDA. Integrità CONFERMATA. ✅
----------------------------------------------------------------------
⚠️---- 1.2 Tentativo di Manomissione UPDATE (come app_user) ----
ℹ️ Tentativo di UPDATE del document_hash del blocco ID: 2 (Firmatario: Marianna) come 'app_user'.
✅ SUCCESSO: Tentativo di UPDATE BLOCCATO dal DB per 'app_user' come previsto!
ℹ️ Errore DB (SQLSTATE 42501): permission denied for table signature_chain
... (output scenario superuser con manomissione e fallimento verifica crittografica) ...
🏁Fine della dimostrazione.
Code language: HTML, XML (xml)
Questo output guida l’utente attraverso le fasi della dimostrazione, evidenziando i successi (✅
) e i fallimenti (⚠️
) in modo chiaro. Si osserverà come il database blocchi i tentativi di manomissione da parte dell’utente con permessi limitati (grazie alla RLS!), e come il meccanismo di verifica della catena rilevi le alterazioni qualora un super utente riesca a modificarla (poiché la crittografia, per sua natura, non ammette falsificazioni!).
Appendice: considerazioni sugli algoritmi crittografici
In questa Proof of Concept (PoC), sono stati utilizzati i seguenti algoritmi crittografici:
- Algoritmo di Hash: SHA-256
- Algoritmo di Firma Digitale: RSA con chiavi a 2048 bit (padding PKCS1v15)
Queste scelte rappresentano una base solida e ampiamente compatibile per molte applicazioni. Tuttavia, a seconda dei requisiti specifici di sicurezza, prestazioni e longevità del sistema, è possibile e spesso consigliabile considerare algoritmi più robusti o differenti.
Alternative per l’Hashing
- SHA-512: Parte della famiglia SHA-2, produce un hash di 512 bit, offrendo una maggiore resistenza teorica alle collisioni rispetto a SHA-256. Può essere più performante su architetture a 64 bit.
- SHA-3 (es. SHA3-256, SHA3-512): Uno standard più recente con un design interno diverso da SHA-2, considerato molto sicuro.
- BLAKE2/BLAKE3: Algoritmi moderni noti per l’alta velocità e sicurezza. BLAKE3 è particolarmente efficiente e parallelizzabile.
Alternative per la Firma Digitale
- RSA con chiavi più lunghe (es. 3072 o 4096 bit): Aumentare la lunghezza della chiave RSA ne incrementa la robustezza contro attacchi computazionali. RSA 2048 bit è generalmente sicuro, ma per requisiti di sicurezza più elevati o per una protezione a più lungo termine, chiavi più lunghe sono preferibili. Questo comporta un aumento del carico computazionale.
- Elliptic Curve Cryptography (ECC):
- ECDSA (Elliptic Curve Digital Signature Algorithm): Offre un livello di sicurezza paragonabile a RSA ma con chiavi significativamente più corte (es. una chiave ECC a 256 bit equivale circa a una RSA a 3072 bit). Questo si traduce in chiavi più piccole e operazioni di firma/verifica potenzialmente più veloci. Curve comuni includono
P-256
(NIST),P-384
,P-521
. - EdDSA (Edwards-curve Digital Signature Algorithm, es. Ed25519): Un altro schema di firma basato su curve ellittiche, apprezzato per la sua sicurezza e per essere meno suscettibile a errori di implementazione rispetto a ECDSA.
- ECDSA (Elliptic Curve Digital Signature Algorithm): Offre un livello di sicurezza paragonabile a RSA ma con chiavi significativamente più corte (es. una chiave ECC a 256 bit equivale circa a una RSA a 3072 bit). Questo si traduce in chiavi più piccole e operazioni di firma/verifica potenzialmente più veloci. Curve comuni includono
Fattori da Considerare per la Scelta
- Livello di Sicurezza Richiesto: Valutare la sensibilità dei dati e il periodo per cui la sicurezza deve essere garantita.
- Prestazioni: Algoritmi più complessi o con chiavi più lunghe possono avere un impatto sulle prestazioni. ECC/EdDSA spesso offrono un buon bilanciamento.
- Compatibilità e Standard: Considerare gli standard di settore, le normative applicabili e la compatibilità con gli altri sistemi.
- Supporto delle Librerie: Verificare che le librerie crittografiche utilizzate (come
cryptography
in Python) supportino pienamente gli algoritmi scelti. - Resistenza Quantistica: È importante notare che gli algoritmi classici come RSA, ECC, SHA-2 e SHA-3 non sono considerati resistenti ad attacchi da parte di computer quantistici su larga scala. La crittografia post-quantistica (PQC) è un campo di ricerca attivo per affrontare questa futura minaccia. Per sistemi che richiedono una sicurezza a lunghissimo termine, monitorare gli sviluppi in PQC è fondamentale.
La scelta degli algoritmi crittografici è una decisione critica che dovrebbe essere basata su un’attenta analisi dei rischi e dei requisiti specifici del progetto.
Conclusioni
La Signature Chain offre una soluzione potente per garantire autenticità, integrità e sequenzialità nei documenti digitali. Attraverso la crittografia e una struttura a catena immutabile, garantisce l’integrità dei documenti e la verificabilità delle firme.
La Proof of Concept, sebbene semplificata con chiavi RSA in memoria, dimostra il potenziale di questa tecnologia. L’integrazione con HSM, key vault, Timestamp Authority e certificati validi ne eleva la sicurezza, rendendola essenziale per sistemi ad alta affidabilità.
In un panorama digitale in continua evoluzione, la Signature Chain è uno strumento fondamentale per costruire fiducia e trasparenza nei flussi di lavoro documentali. La sua traccia di audit e crittograficamente sicura la rende indispensabile per settori come finanza, pubblica amministrazione, sanità e legale. La ricerca sulla resistenza quantistica sarà cruciale per garantirne la longevità.