La foto di un passaporto è il file più sensibile che la maggior parte delle app riceverà mai. Contiene un volto, un nome completo, una data di nascita e un numero di documento, e basta per aprire un conto da qualche altra parte. Eppure in molti flussi di upload l'immagine viene copiata cinque o sei volte prima che qualcuno si chieda se debba esistere.
Questo articolo guarda alla questione dal lato dell'ingegneria. Cita ciò che il GDPR dice sulla conservazione dei dati, passa in rassegna i punti in cui l'immagine di un passaporto si accumula senza che nessuno se ne accorga e descrive uno schema che chiamiamo leggi, restituisci, dimentica: l'immagine viene letta, il risultato viene restituito e dell'immagine non resta nulla. Si chiude con la parte che resta compito vostro, perché alcune aziende sono tenute a conservarne una copia, e da luglio 2027 l'UE lo mette nero su bianco in una nuova legge.
Questo è il blog di doc.cheap, un'API OCR per passaporti e documenti d'identità che legge i documenti d'identità e restituisce il risultato in JSON. Leggete le parti sul prodotto tenendone conto.
Cosa chiede davvero il GDPR
Il GDPR non dice "non conservate mai l'immagine di un passaporto". Dice qualcosa di più utile: tenete ciò che vi serve, per il tempo che vi serve, e non oltre. L'Art. 5(1) del Reg. (EU) 2016/679 fissa i principi. Due di essi decidono gran parte del progetto.
| Principio | Testo dell'Art. 5(1) (versione inglese) | Cosa significa per un'immagine |
|---|---|---|
| Minimizzazione dei dati, lettera (c) | "adequate, relevant and limited to what is necessary in relation to the purposes for which they are processed" | Se il vostro processo ha bisogno del nome, della data di nascita e del numero del documento, l'immagine in sé potrebbe non essere più necessaria una volta letti questi dati. |
| Limitazione della conservazione, lettera (e) | "kept in a form which permits identification of data subjects for no longer than is necessary for the purposes for which the personal data are processed" | Ogni copia ha bisogno di una data di fine, e "non siamo mai riusciti a cancellarla" non lo è. |
L'Art. 5(2) aggiunge la responsabilizzazione: dovete essere in grado di dimostrare che rispettate questi principi. E l'Art. 25(1), la protezione dei dati fin dalla progettazione, chiede "appropriate technical and organisational measures, such as pseudonymisation, which are designed to implement data-protection principles, such as data minimisation, in an effective manner", cioè misure tecniche e organizzative adeguate, come la pseudonimizzazione, pensate per attuare in modo efficace principi come la minimizzazione dei dati.
Letti insieme, trasformano una domanda giuridica in una domanda di ingegneria. Meno copie di un'immagine esistono, meno posti dovete descrivere, proteggere, includere nei backup e prima o poi svuotare. Una copia che non è mai stata scritta è l'unica che non richiede nessuno di questi lavori.
Dove finiscono le immagini dei passaporti
La maggior parte dei team conserva l'immagine di proposito in un solo posto. Il problema sono i posti che nessuno ha scelto. Ecco un elenco da confrontare con il vostro flusso.
| Posto | Come ci arriva l'immagine |
|---|---|
| Bucket di upload | Il client carica prima su un object storage e il backend legge il file da lì. L'oggetto sopravvive alla richiesta. |
| Log delle richieste | Un middleware di logging scrive il corpo delle richieste, e un'immagine in base64 è un corpo di richiesta. |
| Segnalazioni di errore | Un tracker delle eccezioni allega il payload della richiesta fallita. |
| Code e retry | Il messaggio di un job contiene l'immagine, e una dead-letter queue tiene quelli falliti per settimane. |
| Backup e snapshot | Uno snapshot del database o del disco fatto quel giorno contiene ogni immagine scritta prima, molto dopo che la riga è stata cancellata. |
| Ticket di assistenza | Un utente rimanda la foto per email "perché l'upload non ha funzionato". |
| Analytics e session replay | Uno strumento registra la pagina, compresa l'anteprima del file selezionato. |
| Il fornitore OCR | Il servizio che legge il documento tiene una propria copia, secondo le proprie regole di conservazione. |
L'ultima riga è quella che controllate di meno. I vostri log potete sistemarli. Una copia tenuta da un fornitore dipende dalle impostazioni di quel fornitore, e dovete chiedere quali sono.
Lo schema: leggi, restituisci, dimentica
Lo schema è semplice da enunciare. L'immagine esiste solo in memoria, per la durata di una richiesta. Ciò che esce dalla richiesta è la lettura: i valori estratti. L'immagine non esce affatto.
- Inviate l'immagine direttamente al riconoscimento. Nessun bucket di upload nel mezzo. Se per i file grandi un bucket è inevitabile, date all'oggetto una durata di pochi minuti e cancellatelo quando la chiamata risponde.
- Usate subito ciò che vi serve dalla risposta. I ritagli, come la foto del titolare, esistono solo in quella risposta. Se il vostro flusso confronta un selfie con il ritratto, fatelo adesso.
- Tenete la lettura, non l'immagine. Salvate i campi che servono al vostro processo, secondo la vostra regola di conservazione. Una data di nascita e un numero di documento restano dati personali, quindi anche loro hanno una data di fine.
- Tenete l'immagine fuori da log e segnalazioni di errore. Eliminate i corpi delle richieste al confine, in un solo punto, invece di contare sul fatto che ogni chiamante se ne ricordi.
- Mettete per iscritto il risultato. Per ogni posto della tabella qui sopra, annotate se l'immagine può arrivarci e perché no. Quella nota è la responsabilizzazione che chiede l'Art. 5(2).
Cosa fa la nostra API con l'immagine
Ecco come doc.cheap affronta la stessa domanda, come la descrive la pagina su conservazione dei dati e privacy.
- L'immagine non viene mai conservata. Vive in memoria per la durata della richiesta, viene passata al motore di riconoscimento e sparisce quando la risposta è scritta. Nessun disco, object store o log la riceve.
- Nemmeno i ritagli vengono conservati. Il ritaglio del documento, la foto del titolare e la firma tornano nella risposta della chiamata che li ha prodotti. Una scansione riletta in seguito tramite
GET /v1/scans/{id}ha ogni campo immagine impostato anull. - Ciò che si può tenere è la lettura, e solo per la finestra che chiedete. L'opzione
retain_hoursla imposta per ogni richiesta, da 0 fino a 8760 ore (un anno). Un valore esplicito prevale sempre sull'impostazione dell'account. retain_hours: 0non scrive nulla. Non una riga che scade subito: nessuna riga. Non c'è niente da ripulire, niente in un backup e niente da esportare. La scansione conta comunque come scansione.- Il resto lo copre il valore predefinito dell'account. Quando una richiesta non indica una finestra, vale l'impostazione della cronologia dell'account: 24 ore, 7 giorni, 1 mese o 1 anno. I nuovi account partono da 1 anno, così la dashboard mostra una cronologia. Accorciare l'impostazione vale anche per le righe già salvate, ciascuna misurata dal proprio momento di creazione.
- Una riga conservata tiene una piccola immagine: una miniatura di al massimo 96 px sul lato più lungo e al massimo 16 KiB, mostrata nel registro delle operazioni della dashboard perché una riga si possa riconoscere. Non è leggibile tramite l'API. La miniatura se ne va con la riga.
- Una singola scansione si può cancellare prima. Una chiave live invia
DELETE /v1/scans/{id}, che rimuove il risultato, la riga della cronologia e la miniatura. È definitivo.
La guida su come controllare la conservazione della cronologia spiega le impostazioni passo per passo, e la nostra pagina su come trattiamo i dati ne dà il riepilogo.
Ecco una chiamata a conservazione zero in Python con requests. La chiave sandbox pubblica sk_sandbox_public è riportata nella documentazione e non richiede registrazione: dà 10 documenti riconosciuti gratuiti per indirizzo in totale e al massimo 10 richieste all'ora. La conservazione zero è un'impostazione del suo account, quindi richiede la sua chiave live. La sandbox pubblica non è un account: tiene traccia di ogni scansione, con la sua piccola immagine, nel registro del servizio stesso, quindi le invii un'immagine di prova, mai un documento reale.
import base64
import uuid
import requests
API = "https://api.doc.cheap/v1/scans"
KEY = "sk_sandbox_public" # in produzione la vostra chiave live
def read_and_forget(path):
with open(path, "rb") as f:
image = base64.b64encode(f.read()).decode("ascii")
response = requests.post(
API,
headers={
"Authorization": f"Bearer {KEY}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"image": image,
# 0: con una chiave live, lato API non viene scritto nulla su questa scansione.
# False: nessun ritaglio del ritratto, perché questo flusso non lo usa.
"options": {"retain_hours": 0, "return_portrait": False},
},
timeout=30,
)
response.raise_for_status()
scan = response.json()
del image # la copia locale sparisce appena la chiamata risponde
if scan["meta"]["status"] != "recognized":
return None
# Tenete la lettura che serve al vostro processo, secondo la vostra regola di conservazione.
return {
"scan_id": scan["meta"]["id"],
"document_number": scan["document"]["number"],
"expiry_date": scan["document"]["expiry_date"],
"birth_date": scan["holder"]["birth_date"],
"mrz_status": scan["mrz"]["status"],
}
La conservazione zero ha un costo che conviene conoscere prima che vi sorprenda. Di norma una Idempotency-Key permette a un retry di restituire il primo risultato. Con retain_hours: 0 non c'è nessun risultato salvato da restituire, quindi per 24 ore un retry con la stessa chiave viene rifiutato con HTTP 409 e il codice idempotency_replay_unavailable, invece di ricevere una seconda risposta. Interpretate quella risposta come "la prima chiamata è andata a buon fine" e usate il risultato che avete già.
Rileggere la scansione mostra l'altro lato del progetto. Una chiave sandbox non rilegge proprio nulla, qualunque sia l'id. Abbiamo inviato questa richiesta con sk_sandbox_public il 5 ottobre 2026:
curl https://api.doc.cheap/v1/scans/<SCAN_ID> \
-H "Authorization: Bearer sk_sandbox_public"
È tornata con HTTP 404 (il messaggio è accorciato):
{
"error": {
"code": "not_found",
"message": "No scan with id …",
"docs_url": "https://doc.cheap/docs/errors/not_found"
}
}
Una chiave live riceve lo stesso 404 per una scansione fatta con retain_hours: 0, e per qualsiasi scansione una volta trascorsa la sua finestra. Se state confrontando i servizi su questo punto, il confronto tra API OCR per passaporti è un buon punto di partenza; chiedete a ciascuno dove va l'immagine, non solo cosa restituisce.
Cosa resta compito vostro
Leggi, restituisci, dimentica elimina le copie lato API. Non decide cosa la vostra azienda deve conservare. Per alcune aziende la risposta è "una copia", e lo dice la legge.
La nuova legge antiriciclaggio dell'UE, il Reg. (EU) 2024/1624, si applica dal 10 luglio 2027. L'Art. 90 dice: "It shall apply from 10 July 2027, except in relation to obliged entities referred to in Article 3, points (3)(n) and (o), to which it shall apply from 10 July 2029." Cioè si applica dal 10 luglio 2027, tranne che per i soggetti obbligati dell'Art. 3, punti (3)(n) e (o), per i quali si applica dal 10 luglio 2029. Il suo Art. 77 sulla conservazione dei documenti impone ai soggetti obbligati, come le banche e altre società finanziarie, di conservare:
"a copy of the documents and information obtained in the performance of customer due diligence pursuant to Chapter III, including information obtained through electronic identification means;"
In altre parole, una copia dei documenti e delle informazioni ottenuti nell'adeguata verifica della clientela, comprese quelle ottenute con mezzi di identificazione elettronica.
L'Art. 77(3) fissa la durata: i documenti sono "retained for a period of 5 years commencing on the date of the termination of the business relationship", cioè conservati per 5 anni dalla fine del rapporto d'affari, e poi "obliged entities shall delete personal data upon expiry of the five-year period", ossia allo scadere dei cinque anni i dati personali vanno cancellati. L'Art. 77(2) consente, a determinate condizioni, "a retention of the references to such information" al posto delle copie, cioè la conservazione dei soli riferimenti a quelle informazioni.
Quindi, se siete un soggetto obbligato, la conservazione zero sull'API non elimina il vostro dovere di tenere una documentazione. Cambia il posto in cui quella documentazione vive. Il vostro archivio diventa l'unica copia, e il principio di limitazione della conservazione visto sopra vale anche per esso: cinque anni dopo la fine del rapporto, se ne va. Il lavoro di progettazione consiste nel rendere quell'archivio una scelta deliberata, con un solo posto, un solo responsabile, cifratura, controllo degli accessi e un job di cancellazione, invece del mucchio accidentale della tabella qui sopra.
Se non siete un soggetto obbligato, ponetevi prima la domanda semplice: c'è qualcosa nel vostro processo che ha bisogno dell'immagine dopo che i campi sono stati letti? Spesso la risposta onesta è no.
Una checklist
- Ogni posto della tabella "dove finiscono le immagini" è stato confrontato con il vostro flusso.
- L'immagine va direttamente al riconoscimento, oppure passa da un bucket con una durata di pochi minuti.
- I ritagli si usano dentro il gestore della risposta e non vengono scritti da nessuna parte.
- La chiamata OCR imposta la sua conservazione di proposito,
retain_hours: 0quando non serve rileggere nulla. - I retry gestiscono la risposta 409
idempotency_replay_unavailable. - Log e segnalazioni di errore eliminano i corpi delle richieste in un unico punto di confine.
- I campi che tenete hanno una data di fine, e qualcosa li cancella.
- Se una legge richiede una copia, questa vive in un unico archivio deliberato con la propria data di cancellazione.
Questo è un riepilogo tecnico, non una consulenza legale. Se trovate un punto da cui un'immagine può sfuggire e che questo articolo non considera, scrivete a admin@doc.cheap.
Una domanda per voi: dove avete trovato l'ultima volta la copia di un documento d'identità che nessuno intendeva conservare? Raccontatecelo nei commenti qui sotto.