Aggiornato

Crittografia Zero-Knowledge: Come Vaulted Mantiene i Tuoi Segreti Privati

Di

La crittografia zero-knowledge significa che il server non vede mai il tuo testo in chiaro né le tue chiavi di crittografia. Vaulted lo realizza cifrando ogni segreto nel tuo browser con AES-256-GCM prima che qualsiasi dato raggiunga il server. Il server memorizza esclusivamente il testo cifrato — non ha accesso alla chiave di decrittazione, che risiede unicamente nel frammento URL.

Questo articolo illustra l'implementazione crittografica esatta: come vengono generate le chiavi, come vengono cifrati i dati, come la chiave raggiunge il destinatario e cosa succede quando aggiungi una passphrase. Ogni dettaglio qui rispecchia il codice reale in esecuzione nel tuo browser.

Per una panoramica generale su perché la crittografia lato client è importante, consulta il nostro articolo precedente sulla crittografia lato client. Questo articolo approfondisce le primitive crittografiche.

Generazione della chiave

Ogni volta che crei un segreto, Vaulted genera una nuova chiave di crittografia AES-256-GCM utilizzando la Web Crypto API:

crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [
  'encrypt',
  'decrypt',
]);

Alcune cose da notare:

  • AES-GCM (Galois/Counter Mode) garantisce sia la riservatezza che l'integrità — rileva le manomissioni, non solo le letture non autorizzate.
  • Lunghezza della chiave a 256 bit è l'opzione più robusta disponibile per AES ed è utilizzata da governi e istituti finanziari in tutto il mondo.
  • Extractable: true — la chiave può essere esportata come byte grezzi in modo da poter essere inserita nel frammento URL per il destinatario.

La Web Crypto API delega al motore crittografico nativo del browser (ad es. BoringSSL in Chromium, NSS in Firefox). La chiave viene generata in modo sicuro in memoria e non tocca mai il disco né la rete.

Cifratura

Con la chiave generata, Vaulted cifra il tuo testo in chiaro:

const iv = crypto.getRandomValues(new Uint8Array(12));

crypto.subtle.encrypt(
  { name: 'AES-GCM', iv },
  key,
  new TextEncoder().encode(plaintext),
);

Il vettore di inizializzazione (IV): AES-GCM richiede un IV casuale di 12 byte (96 bit) per ogni operazione di cifratura. L'IV non deve essere segreto — viene memorizzato insieme al testo cifrato — ma non deve mai essere riutilizzato con la stessa chiave. Poiché Vaulted genera una nuova chiave per ogni segreto, il riutilizzo dell'IV è impossibile per design.

Codifica: Sia il testo cifrato che l'IV vengono codificati come stringhe base64url (base64 standard con + sostituito da -, / sostituito da _ e i = finali rimossi). Questo li rende sicuri da includere in URL e payload JSON.

Il testo cifrato e l'IV vengono inviati al server per la memorizzazione. Il testo in chiaro non lascia mai il browser.

Consegna della chiave tramite frammento URL

Dopo la cifratura, la chiave viene esportata e inserita nel frammento URL — la parte dopo #:

const rawKey = await crypto.subtle.exportKey('raw', key);
// rawKey → base64url-encoded string

Il link cifrato risultante ha questo aspetto:

https://vaulted.fyi/s/abc123#dGhpcyBpcyBhIGtleQ
                              ^^^^^^^^^^^^^^^^^^^^^^^^
                              encryption key (base64url)

Il separatore # è fondamentale. Secondo RFC 3986, l'identificatore di frammento viene elaborato interamente dal client. I browser non includono mai il frammento nelle richieste HTTP — né nell'URL, né nell'intestazione Referer, né in nessun altro posto visibile al server.

Quando il destinatario apre il link:

  1. Il browser richiede /s/abc123 al server (senza frammento)
  2. Il server restituisce il testo cifrato e l'IV
  3. JavaScript legge il frammento da window.location.hash
  4. La chiave viene importata nuovamente nella Web Crypto API
  5. Il testo cifrato viene decrittato localmente

Il server facilita l'archiviazione e il recupero del blob cifrato. Non partecipa mai alle operazioni crittografiche.

Protezione opzionale con passphrase

Per i segreti che necessitano di protezione aggiuntiva, Vaulted supporta il key wrapping basato su passphrase. Questo aggiunge un secondo livello: anche se qualcuno intercetta il link, non può decifrare il segreto senza la passphrase.

Derivazione della chiave con PBKDF2

Quando imposti una passphrase, Vaulted deriva una chiave di wrapping utilizzando PBKDF2:

crypto.subtle.deriveKey(
  {
    name: 'PBKDF2',
    salt, // 16 bytes, randomly generated
    iterations: 600_000,
    hash: 'SHA-256',
  },
  keyMaterial, // the passphrase, imported as raw key material
  { name: 'AES-KW', length: 256 },
  false,
  ['wrapKey', 'unwrapKey'],
);
  • 600.000 iterazioni rendono gli attacchi brute-force computazionalmente costosi.
  • Salt casuale da 16 byte garantisce che passphrase identiche producano chiavi derivate diverse per segreti diversi.
  • SHA-256 è la funzione hash utilizzata in ogni iterazione PBKDF2.
  • La chiave derivata è una chiave AES-KW (AES Key Wrap, RFC 3394), non una chiave AES-GCM. Il key wrapping è un'operazione distinta dalla cifratura.

Wrapping della chiave di cifratura

La chiave AES-KW derivata esegue il wrapping della chiave di cifratura originale:

crypto.subtle.wrapKey('raw', encryptionKey, wrappingKey, 'AES-KW');

Il frammento URL contiene ora sia la chiave sottoposta a wrapping che il salt, separati da un punto:

#wrappedKeyBase64url.saltBase64url

Unwrapping durante la decrittazione

Quando il destinatario inserisce la passphrase, Vaulted inverte il processo:

  1. Il frammento viene diviso su . per ottenere la chiave sottoposta a wrapping e il salt
  2. La stessa chiave di wrapping AES-KW viene derivata con PBKDF2 dalla passphrase e dal salt
  3. La chiave di cifratura viene recuperata con crypto.subtle.unwrapKey
  4. Il testo cifrato viene decrittato con la chiave AES-GCM recuperata

Se la passphrase è errata, unwrapKey genera un errore — non vi è decrittazione parziale né perdita di informazioni.

Flusso di decrittazione

Senza passphrase, la decrittazione è diretta:

  1. Importa la chiave dal frammento URL (crypto.subtle.importKey)
  2. Decrittа il testo cifrato con l'IV (crypto.subtle.decrypt)
  3. Decodifica il testo in chiaro dai byte UTF-8

Con una passphrase:

  1. Dividi il frammento per estrarre la chiave sottoposta a wrapping e il salt
  2. Deriva la chiave di wrapping dalla passphrase e dal salt (PBKDF2)
  3. Recupera la chiave di cifratura (AES-KW)
  4. Decrittа il testo cifrato (AES-GCM)
  5. Decodifica il testo in chiaro

Tutto questo avviene nel browser. Il server restituisce il blob cifrato e i metadati (conteggio visualizzazioni, scadenza) — nient'altro.

Modello di minaccia

Nessuno strumento di sicurezza protegge da tutto. Ecco contro cosa è progettata l'architettura di Vaulted e cosa esula dal suo ambito.

Da cosa protegge Vaulted

  • Compromissione del server — Il server memorizza solo testo cifrato. Senza la chiave (che non viene mai inviata al server), i dati sono illeggibili. Un dump completo del database produce solo blob cifrati.
  • Violazione del database — Come sopra. Il testo cifrato senza chiavi è computazionalmente inutile con AES-256-GCM.
  • Man-in-the-middle lato server — La chiave di cifratura risiede nel frammento URL, che non viene mai trasmesso nelle richieste HTTP. TLS protegge il testo cifrato in transito.
  • Operatori del servizio che leggono i segreti — Zero-knowledge significa esattamente questo. Non possiamo leggere i tuoi segreti anche se costretti a farlo, perché non abbiamo le chiavi.

Da cosa Vaulted NON protegge

  • Browser o dispositivo compromesso — Se il dispositivo del destinatario contiene malware o un keylogger, il testo in chiaro decrittato può essere catturato dopo la decrittazione.
  • Estensioni del browser dannose — Le estensioni con accesso alla pagina possono leggere il DOM dopo la decrittazione, incluso il segreto rivelato.
  • Intercettazione del link — L'URL contiene la chiave di cifratura nel frammento. Chiunque ottenga l'URL completo (incluso il frammento) può decifrare il segreto. Condividi i link tramite canali sicuri.
  • Shoulder surfing — Una volta che il segreto è visualizzato sullo schermo, chiunque stia guardando può vederlo.
  • Comportamento del destinatario — Dopo la decrittazione, il destinatario può fare screenshot, copiare o condividere il testo in chiaro. Vaulted non può impedirlo.

La funzione passphrase mitiga il rischio di intercettazione del link: anche con l'URL completo, l'attaccante ha comunque bisogno della passphrase. Condividi la passphrase tramite un canale diverso (una telefonata, un messaggio separato) per una strategia di difesa in profondità.

Riepilogo

La crittografia zero-knowledge di Vaulted significa:

  1. Viene generata una nuova chiave AES-256-GCM per ogni segreto
  2. La cifratura avviene interamente nel tuo browser
  3. Il server memorizza solo testo cifrato
  4. La chiave di decrittazione viaggia esclusivamente nel frammento URL, che i browser non inviano mai ai server
  5. Il wrapping opzionale con passphrase aggiunge un secondo livello tramite PBKDF2 e AES-KW

Il risultato: anche se tutti i server, i database e i percorsi di rete fossero compromessi simultaneamente, i tuoi segreti rimarrebbero cifrati. Le chiavi esistono solo nei link che condividi.


Pronto a condividere un segreto in modo sicuro? Crea un segreto su Vaulted — ci vogliono 10 secondi.


Correlati