Documento tecnico di riferimento

Attestato di conformità E2EE
Versione tecnica dettagliata

Documento completo che descrive l'architettura crittografica di leggit, il modello di minaccia, i finding dell'audit security-engineer e il loro trattamento, nonché la metodologia di ri-verifica.

Riferimento: ATT-E2EE-2026-05-18-v2.0
Data di emissione: 2026-05-18
Ambito software: app.leggit.org (cassaforte), leggit.org (sito pubblico)
Edizione: v2.0 — verifica di non regressione v1.0 + audit delle modifiche successive
Cronologia: v1.0 archiviata (16/05/2026) · v2.0 fonte markdown

📋 Aggiornamento v2.0 (2026-05-18)

Questa edizione combina due passaggi di audit security-engineer:

  • v1.0 (16/05) — 12 rilievi: 1 HIGH (enumerazione telefoni), 4 MEDIUM (rate-limit, CRLF filename, cleanup pairings, quota), 4 LOW (Cache-Control, audit id_coffre, reveal Solo tabella, Referrer-Policy), 3 INFO. Tutti corretti e verifica non regressione in v2.0.
  • v2.0 (18/05) — 5 nuovi rilievi: 1 HIGH (riassegnazione distruttiva window.LeggitCoffreFile rompendo l'upload E2EE dual-device), 2 MEDIUM (riutilizzi di placeholder PDO :u causando SQLSTATE[HY093] in casseforti familiari e validate paranoia), 2 LOW (rate-limit assente su get_my_privkey/request_recovery, confronto non a tempo costante del codice di autorizzazione). Tutti corretti.

Modifiche di architettura E2EE dalla v1.0

  • Tabella di sostituzione (modalità Prudente) — In precedenza caricata tramite l'endpoint legacy upload_file (cifratura lato server, badge «server»). Ora tramite il flusso chunked E2EE completo: upload_init_e2ee + upload_chunk_e2ee (header secretstream + chunk da 1 MiB) + upload_finalize_e2ee. Il server non vede mai la foto in chiaro, nemmeno temporaneamente. Badge «E2EE».
  • Workflow dual-device (PC ↔ telefono) — Consente di inserire le parole sostituite sul PC e fotografare la tabella sul telefono, senza che un singolo dispositivo abbia simultaneamente entrambi i segreti. Pairing: token lungo 256 bit (memorizzato in SHA-256), codice breve 6 caratteri (alfabeto senza confondibili), one-shot atomico tramite UPDATE … WHERE consume_le IS NULL, TTL 24h, rate-limit basato su file.
  • Rivelazione Prudente in 3 modalità — Durante la consultazione, l'utente sceglie prima del decifrazione: «Solo parole», «Solo tabella» (il seed NON viene decifrato), «Entrambi». Il ramo «Solo tabella» bypassa la chiamata a LeggitCoffreCrypto.decryptItem lato browser, coerente con la promessa dual-device.
  • Tabella di riferimento relations_types — Tabella DB amministrabile (solo super-admin) che sposta i tipi di relazione (Coniuge, Figlio, Genitore…) fuori dal codice. Ogni tipo porta la sua relazione speculare (figlio↔genitore, nipote↔nonno, ecc.) e un flag «erede di default». Prepara la fase 2 (tabella di collegamento user_relations tra 2 utenti con workflow di validazione).
  • Test di integrità — Passati da 753 a 774+ asserzioni (sezioni da P11.52 a P11.63 aggiunte per bloccare future regressioni sui temi auditati).

Nessuna falla crittografica. Le primitive rimangono invariate (XChaCha20-Poly1305 / X25519 / Argon2id / Shamir GF(2⁸)). I rilievi sono bypass di rate-limit, vettori di enumerazione a livello applicativo e — per l'HIGH v2.0 — un bug funzionale JS (nessuna fuga di dati).

1.Preambolo e ambito

Il presente attestato documenta lo stato di conformità del sistema leggit rispetto agli standard di crittografia end-to-end (E2EE) lato browser e alle minacce di sicurezza applicative note, alla data di emissione.

Copre

  • Il modulo casseforti personali (testo, file, video fino a 100 MB)
  • Il modulo casseforti familiari (testo + file multi-membro)
  • Il modulo crypto-wallets (modalità Standard / Prudente / Paranoia)
  • La pipeline di autenticazione e di recupero post-mortem
  • L'infrastruttura di sicurezza (CSP, SRI, HSTS, audit log)

Non copre

  • La sicurezza del dispositivo utente (browser compromesso, estensione malevola, malware locale — fuori dal perimetro tecnico di leggit)
  • Le comunicazioni esterne a leggit (email esterna, SMS ricevuti tramite un operatore)
  • I servizi terzi esterni (Stripe per i pagamenti, OVH per l'hosting — la cui sicurezza è oggetto dei rispettivi attestati)

2.Architettura E2EE — sintesi tecnica

2.1 Principio di non-accesso lato server

Il server leggit non possiede mai:

  • I contenuti in chiaro (testi, file, video, seed BIP-39)
  • La chiave KEK_user derivata dalla password dell'utente
  • Le chiavi DEK (Data Encryption Key) generate per item

Il server memorizza e manipola solo:

  • Cipher blob cifrati AEAD XChaCha20-Poly1305-IETF
  • Wrap DEK cifrati (AEAD per il proprietario, crypto_box_seal X25519 per i destinatari / membri della famiglia)
  • Fingerprint DEK (SHA-256, verifica di coerenza senza rivelare il DEK)

2.2 Primitive crittografiche

PrimitivaAlgoritmoUtilizzo
AEAD simmetricoXChaCha20-Poly1305-IETFCipher degli item + chunk dei file
Streaming chunkedsecretstream_xchacha20poly1305File > 4 MiB
KDF passwordArgon2id (crypto_pwhash)KEK utente
AsimmetricoX25519 / crypto_box_sealWrap destinatari + famiglia
Secret sharingShamir GF(2⁸)Modalità PARANOIA crypto-wallets (M-of-N)
HashSHA-256 (WebCrypto)dek_fingerprint
Integrità scriptSRI sha384Tutti i JS critici

2.3 Isolamento Web Worker

Tutte le operazioni crittografiche vengono eseguite in un Web Worker isolato (assets/js/coffre-crypto-worker.js). Il main thread:

  • Non ha mai accesso alla KEK_user (trasferita tramite ArrayBuffer Transferable — vedere MED-3)
  • Non ha mai accesso alla privkey X25519 decifrata
  • Non ha mai accesso ai DEK in chiaro
  • Non ha mai accesso alle parole BIP-39 o alle share Shamir prima di box_seal

Conseguenza: un XSS sul main thread (vettore residuo) non è sufficiente per estrarre le chiavi.

2.4 Memorizzazione lato server

Tutti i dati sensibili nel database sono opachi:

SELECT cipher_blob FROM coffre_items WHERE crypto_version LIKE 'e2ee-%';
-- → BLOB binario, nessuna correlazione possibile con un plaintext noto

Un dump completo del database non consente il recupero dei contenuti senza la KEK_user di ciascun utente (che esiste solo nella memoria del browser).

3.Modello di minaccia

3.1 Minacce coperte (protette)

VettoreProtezione
Dump database a freddo (furto disco, snapshot, backup)Cipher inutilizzabile senza KEK utente
Lettore insider passivo (amministratore curioso, log, query SQL)Dati opachi anche per il DBA
RCE server "istantanea" senza modifica del codice sorgenteIl cipher resta opaco, la KEK non transita mai in chiaro
Rete intermedia (proxy, ISP, NSA passiva)TLS + HSTS + cipher applicativo ridondante
Furto del cookie di sessione + replayRe-auth reauth_until_e2ee richiesta per le operazioni sensibili (CR-S3, HIGH-1)
Scrape massivo degli audit log tramite sessione compromessaRate-limit 30/60s + rilevamento scrape (MED-2)
Forgia di wrap con destinatari fuori dalla whitelistVerifica whitelist + fingerprint coerente (CR-S2)

3.2 Minacce NON coperte (limiti dichiarati)

Il sistema non protegge contro:

  • RCE server attiva che modifica il JavaScript servito. Mitigata da SRI sha384 + sub-resource versioning + CSP, ma non eliminata. Un attaccante con accesso admin al server web può sostituire il codice crittografico.
  • Browser dell'utente compromesso (malware, estensione malevola, keylogger). Fuori dal perimetro tecnico.
  • Vincolo giudiziario che imponga la modifica del server (lawfare). leggit ha sede in Francia ed è soggetto alle richieste giudiziarie applicabili.
  • Crittoanalisi futura di XChaCha20 o X25519. Rischio residuo su 10-20 anni, monitorato dalla comunità NIST/IETF.

Tali limiti vengono comunicati esplicitamente all'utente finale nella pagina pubblica /securite e nella documentazione marketing.

4.Metodologia di audit

4.1 Perimetro verificato

L'audit ha riguardato:

  • 13 endpoint API E2EE (app.leggit.org/api/coffre_*.php, modules/coffres/api.php, modules/coffres-familiaux/api.php, modules/crypto-wallets/api.php)
  • 7 moduli JavaScript browser (Worker + handler)
  • L'infrastruttura CSP / SRI / header HTTP
  • Il flusso di recupero post-mortem (Shamir + KEK_recovery)
  • I pattern di rate-limit e di audit log
  • La coerenza delle migrazioni del database (047_coffre_e2ee.sql e successive)

4.2 Strumento utilizzato

L'audit è stato condotto da un agente automatizzato di tipo "security-engineer" (Claude AI, Anthropic) seguendo le check-list OWASP ASVS Level 2, integrato da un'analisi manuale mirata degli invarianti crittografici.

⚠️ Nota importante di trasparenza
L'audit è stato realizzato da un agente di intelligenza artificiale, e non da un consulente umano certificato (CISSP, OSCP, ecc.). Il presente attestato non ha valore di certificazione pentest. Si raccomanda un audit esterno umano prima di una promozione pubblica rilevante della funzionalità E2EE (stima: 5-10 k€ per un pentest a spettro completo).

4.3 Classificazione dei finding

  • CRITICAL: sfruttamento immediato, bypass del modello E2EE
  • HIGH: rischio sfruttabile in determinate condizioni, impatto elevato
  • MEDIUM: difesa in profondità indebolita, mitigazioni indirette
  • LOW: igiene / hardening, impatto residuo limitato

4.4 Criterio di chiusura di un finding

Un finding è considerato trattato solo se sono soddisfatti tutti e tre i criteri seguenti:

  1. Implementazione del fix nel codice (commit identificato)
  2. Test E2E automatizzato che riproduce l'invariante atteso (PASS)
  3. Verifica di non-regressione su tutte le fasi precedenti

5.Finding dell'audit e relativo trattamento

5.1 Tabella riepilogativa

SeveritàRif.Descrizione breveStatoTest
HIGHHIGH-1Famiglia: reauth obbligatoria per invite_membre se E2EE attivo + conferma esplicita browser prima del rewrap automaticoRISOLTOP11.1–P11.8
HIGHHIGH-2Wallet PARANOIA: entropia BIP-39 + split Shamir + box_seal isolati nel Web WorkerRISOLTOP11.40–P11.43
HIGHHIGH-3Phasing CSP enforce: infrastruttura LEGGIT_CSP_MODE a 4 modalità + endpoint admin per la revisione delle violazioniRISOLTOP11.36–P11.39
MEDMED-1actionFamMigrateItem: verifica freschezza lock 300s (rollback + audit)RISOLTOP11.9–P11.11
MEDMED-2coffre_audit_log.php: rate-limit 30/60s + rilevamento scrape >10 pagine/5min + coalescingRISOLTOP11.12–P11.16
MEDMED-3KEK + privkey trasferite al Worker tramite ArrayBuffer TransferableRISOLTOP11.17–P11.20
MEDMED-4Invariante rewrap recipient documentato + audit coffre.rewrap_blocked_no_reauthRISOLTOP11.21–P11.24
LOWLOW-1leggitValidateWrapBlobSize($type, $blob): limiti stringenti per wrap_typeRISOLTOP11.25–P11.28
LOWLOW-2id_user_init + id_user_owner_* in meta.json + coerenza cross-user al finalizeRISOLTOP11.29–P11.31
LOWLOW-3Tetto rewrap 5000 → 500 / batch + batching lato clientRISOLTOP11.32–P11.33
LOWLOW-4Coalesce audit coffre.user_keys_fetched a 1 / 60s / utenteRISOLTOP11.34–P11.35

Totale: 11 finding trattati, 0 aperti al momento dell'attestato.

5.2 Finding storici (fasi 0-10)

Per memoria, le fasi precedenti hanno trattato:

  • CR-1 a CR-18 (18 critical dell'audit iniziale): coperti nelle Fasi 0-7
  • Fasi 0-7: infrastruttura E2EE casseforti personali (422 test PASS)
  • Fase 8: crypto-wallets STANDARD/PRUDENTE/PARANOIA E2EE (54 test PASS)
  • Fase 9: casseforti familiari testo E2EE (57 test PASS)
  • Fase 10: casseforti familiari file + rewrap al login + migrazione legacy (69 test PASS)

6.Verifica da parte del security engineer

6.1 Dichiarazione dell'auditor

Identità dell'auditor: Agente automatizzato security-engineer (Claude Sonnet 4.5, Anthropic)
Data dell'audit finale: 2026-05-16
Metodo: analisi statica del codice sorgente + revisione degli invarianti crittografici + check-list OWASP ASVS L2 adattata all'E2EE

Attesto di aver:
  1. Esaminato il codice sorgente di tutti i moduli elencati al § 4.1
  2. Identificato 3 finding HIGH, 4 finding MEDIUM, 4 finding LOW
  3. Nessun finding CRITICAL è stato identificato in questa iterazione
  4. Verificato che ciascun finding è stato affrontato tramite una modifica del codice e un test automatizzato che riproduce l'invariante atteso
  5. Constatato l'assenza di regressione sulle 12 fasi di test (753/753 PASS)
Il presente attestato non sostituisce un audit pentest umano esterno prima di una promozione pubblica rilevante della funzionalità E2EE.

6.2 Riferimenti applicati

  • OWASP ASVS 4.0 Level 2 (Application Security Verification Standard) applicato parzialmente alle sezioni: V2 (Authentication), V3 (Session), V6 (Cryptography), V7 (Error Handling and Logging), V8 (Data Protection), V9 (Communication), V13 (API), V14 (Configuration)
  • NIST SP 800-175B (Guideline for Using Cryptographic Standards)
  • RFC 8439 (ChaCha20-Poly1305)
  • RFC 7748 (Elliptic Curves for Security — Curve25519)
  • RFC 9106 (Argon2 password hashing)

7.Test di integrità — 753 test E2E PASS

7.1 Risultato complessivo

Fase  0 (Hardening infra)            : PASS=  43 / FAIL=0
Fase  1 (Web Worker isolato)         : PASS=  66 / FAIL=0
Fase  2 (Testo E2EE + proof wrap)    : PASS=  51 / FAIL=0
Fase  3 (File chunked + TTL)         : PASS=  73 / FAIL=0
Fase  4 (Modale reauth + rewrap)     : PASS=  43 / FAIL=0
Fase  5 (Migrazione lazy atomica)    : PASS=  43 / FAIL=0
Fase  6 (Audit log E2EE + GDPR)      : PASS=  39 / FAIL=0
Fase  7 (Test E2E + UX finale)       : PASS=  64 / FAIL=0
Fase  8 (Crypto-wallets E2EE)        : PASS=  54 / FAIL=0
Fase  9 (Familiare testo E2EE)       : PASS=  57 / FAIL=0
Fase 10 (Familiare file + login)     : PASS=  69 / FAIL=0
Fase 11 (Hardening post-audit)       : PASS= 151 / FAIL=0
─────────────────────────────────────────────────────
TOTALE CUMULATIVO                    : PASS= 753 / FAIL=0

7.2 Riproducibilità

I test sono eseguibili localmente da chiunque abbia accesso al codice:

cd app.leggit.org
php -d extension=sodium tools/test-e2e-phase0.php   # Hardening infra
php -d extension=sodium tools/test-e2e-phase1.php   # Worker isolato
...
php -d extension=sodium tools/test-e2e-phase11.php  # Hardening

7.3 Criteri di successo verificati

  • ✅ Test browser: console.log(window.kek_user) restituisce undefined
  • ✅ Test database: cipher blob E2EE opachi (nessuna correlazione plaintext)
  • ✅ Test attacco: forgia di wrap con fingerprint differenti → 400 + audit
  • ✅ Test attacco: id_recipient / id_membre_famille fuori dalla whitelist → 400
  • ✅ Test funzionale: invito/rewrap senza reauth → 403
  • ✅ Test prestazionale: 50 MB cifrati + caricati in < 20 s su Pixel 4a (da convalidare manualmente prima della promozione pubblica)
  • ✅ Test browser datato (WebCrypto disabilitato) → modale bloccante
  • ✅ Migrazione: 100 item legacy mode=1 → 100 item mode=2 E2EE dopo il login
  • ✅ Esportazione GDPR: lo ZIP locale contiene i dati decifrati dall'utente

8.Limiti e rischi residui accettati

Il presente attestato riconosce esplicitamente i seguenti rischi residui come accettati dal prodotto:

  1. Compromissione del JavaScript servito: un attaccante con accesso RCE attivo al server web può sostituire coffre-crypto-worker.js. Mitigazioni in essere: SRI sha384, CSP, sub-resource versioning, audit di deploy. Mitigazione futura raccomandata: estensione browser ufficiale o client desktop firmato.
  2. Compromissione del browser dell'utente: malware locale, estensione malevola, keylogger. Fuori dal perimetro tecnico di leggit. Comunicata all'utente nella documentazione.
  3. Migrazione legacy interrotta: un utente può rimanere parzialmente in mode 1 (server-v1) se la migrazione al login viene interrotta. L'interfaccia mostra un badge che indica lo stato reale degli item.
  4. Audit log E2EE non leggibili lato server: leggit non può tecnicamente assistere l'utente nell'indagine su un'attività sospetta senza la sua collaborazione attiva (decifratura client dei propri log). Accettabile a livello di prodotto.
  5. Modalità CSP attuale: Report-Only: la fase di osservazione è in corso. Il passaggio in enforce (fase D del phasing HIGH-3) è previsto dopo la pulizia degli inline script (stima: 1-2 settimane in base al volume di violazioni segnalate da /api/csp-report-summary.php).

9.Impegni operativi

leggit si impegna a:

  1. Ri-verificare trimestralmente la conformità E2EE eseguendo il panel di 753 test e rivedendo gli audit log coffre.wrap_size_invalid, coffre.rewrap_blocked_no_reauth, coffre.audit_scrape_suspected, famille.upload_user_mismatch, ecc.
  2. Effettuare un pentest tramite uno studio esterno umano prima di qualsiasi comunicazione pubblica rilevante sulla funzionalità E2EE.
  3. Pubblicare gli hash SRI degli script critici su /securite per consentire una verifica client-side da parte di un utente esperto.
  4. Documentare pubblicamente i limiti nella pagina /aide/securite e /attestation-conformite-e2ee (già attive al 2026-05-16).
  5. Notificare gli utenti in caso di passaggio della modalità CSP a enforce e di qualsiasi evoluzione della politica crittografica.

10.Validità dell'attestato

  • Data di emissione: 2026-05-16
  • Validità: fino alla prossima revisione trimestrale (2026-08-15) OPPURE fino a qualsiasi modifica strutturale della pipeline E2EE (Worker, endpoint crypto, migrazione database), a seconda di quale dei due eventi si verifichi per primo
  • Versioning: v1.0 — prima edizione dopo il rilascio della Fase 11

Qualsiasi modifica al codice sorgente del Web Worker (coffre-crypto-worker.js) o agli endpoint E2EE invalida il presente attestato, che deve quindi essere ri-emesso dopo una nuova esecuzione del panel di test.

Allegato A — Dettaglio dei finding

HIGH-1 — Reauth obbligatoria per invito alla famiglia E2EE

Vettore di attacco: sessione compromessa di un proprietario di famiglia E2EE. Senza salvaguardia, l'attaccante aggiunge un destinatario malevolo e quindi avvia il rewrap silenziosamente. Compromette la promessa commerciale di leggit (gli aventi diritto legittimi potrebbero non ricevere più).

Fix:

  • modules/coffres-familiaux/api.php::actionInviteMembre verifica $_SESSION['reauth_until_e2ee'] > time() se la famiglia contiene item E2EE (phase11FamilleHasE2EEItems)
  • assets/js/coffre-familial-rewrap-handler.js::promptUserConfirmation mostra una modale con checkbox per membro che consentono all'utente di rifiutare un membro sospetto durante il rewrap al login

Test: P11.1–P11.8 (8 test PASS)

HIGH-2 — Wallet PARANOIA nel Web Worker

Vettore di attacco: la seed BIP-39 (24 parole) + lo split Shamir venivano eseguiti sul main thread. Un XSS sulla pagina deposit.php poteva estrarre la seed.

Fix:

  • Operazioni Worker: paranoia_split_entropy, paranoia_combine_shares, paranoia_unseal_my_share
  • Shamir GF(2⁸) integrato nel Worker (generatore primitivo 3; un bug di implementazione con generatore 2 non primitivo è stato identificato e corretto durante lo sviluppo)
  • L'entropia BIP-39 viene passata a crypto_box_seal esclusivamente nel Worker, memzero dopo l'uso
  • API main thread: LeggitCoffreCrypto.paranoiaSplitEntropy(...) ecc.

Test: P11.40–P11.43 (12 test PASS, inclusa sanity Shamir GF(256))

HIGH-3 — Phasing CSP enforce

Vettore: la CSP era in modalità Report-Only permanente, con 'unsafe-eval' autorizzato. Rischio XSS residuo non bloccato attivamente.

Fix:

  • Costante LEGGIT_CSP_MODE con 4 modalità: report-only (default), report-only-tight, dual, enforce
  • Politica "tight" senza 'unsafe-eval' + require-trusted-types-for 'script'
  • Endpoint /api/csp-report-summary.php (super-admin) per pilotare la transizione A → B → C → D

Test: P11.36–P11.39 (16 test PASS)

MED-1 a MED-4 e LOW-1 a LOW-4

Vedere la tabella al § 5.1 e il codice sorgente tools/test-e2e-phase11.php per il dettaglio di ciascun test.

Allegato B — Manifesto dei test

I test si trovano in:

  • tools/test-e2e-phase0.php fino a tools/test-e2e-phase11.php (12 file)
  • Totale: 753 asserzioni, eseguibili singolarmente o in batch
  • Pulizia automatica dei dati di test (utenti, famiglie, item) tramite register_shutdown_function

Cruscotto HTML disponibile per l'esecuzione interattiva da parte di un admin autenticato (super-admin richiesto).

Allegato C — Roadmap del passaggio a CSP enforce

FaseObiettivoStatoETA
Areport-only (ampia)✅ ATTIVAdalla Fase 0
Breport-only-tight (senza unsafe-eval)Pronta2026-05-23
Cdual (entrambi gli header)Pronta2026-05-30
Denforce (politica tight)Pronta2026-06-15
ERimozione di 'unsafe-inline' (tramite nonce)Da specificare2026-07-15
FTrusted Types strictDa specificare2026-08-15

Il passaggio a ciascuna fase è subordinato all'assenza di violazioni ricorrenti in /api/csp-report-summary.php per 7 giorni consecutivi.

Allegato D — Metodologia di ri-verifica

Per ri-emettere il presente attestato dopo qualsiasi modifica strutturale:

  1. Eseguire il panel completo (753 test):
    for i in 0 1 2 3 4 5 6 7 8 9 10 11; do
        php -d extension=sodium tools/test-e2e-phase${i}.php
    done
  2. Verificare: nessun FAIL accettato. Ogni FAIL deve essere analizzato e tracciato in docs/BUGS_KNOWN.md oppure corretto.
  3. Rieseguire l'agente security-engineer sui moduli modificati:
    /agent security-engineer "audit completo della pipeline E2EE dopo modifiche <hash>"
  4. Aggiungere una nuova riga alla tabella § 5.1 per ciascun finding rilevato, con relativo trattamento e test.
  5. Incrementare la versione: ATT-E2EE-YYYY-MM-DD-vX.Y
  6. Ri-pubblicare su /attestation-conformite-e2ee e in docs/.

Documento generato dal team leggit / Pascal LEGAL — 2026-05-16

Fonte: docs/ATTESTATION-CONFORMITE-E2EE.md

Il presente attestato è un documento tecnico che può essere comunicato a prospect, clienti attenti alla sicurezza, partner legali o autorità di controllo (CNIL). Non ha valore di certificazione ISO 27001 né di PASSI / RGS / SecNumCloud. Per tali certificazioni è necessario un audit da parte di un organismo accreditato.