Chiffrement zero-knowledge : comment Vaulted garde tes secrets privés
Par Maxim Novak
Le chiffrement zero-knowledge signifie que le serveur ne voit jamais ton texte en clair ni tes clés de chiffrement. Vaulted met cela en œuvre en chiffrant chaque secret dans ton navigateur avec AES-256-GCM avant que la moindre donnée n'atteigne le serveur. Le serveur ne stocke que du texte chiffré — il n'a jamais accès à la clé de déchiffrement, qui vit exclusivement dans le fragment d'URL.
Cet article détaille l'implémentation cryptographique exacte : comment les clés sont générées, comment les données sont chiffrées, comment la clé parvient au destinataire, et ce qui se passe quand tu ajoutes une phrase secrète. Chaque détail ici reflète le code réel qui s'exécute dans ton navigateur.
Pour une vue d'ensemble des raisons pour lesquelles le chiffrement côté client est important, consulte notre article précédent sur le chiffrement côté client. Cet article va plus loin dans les primitives cryptographiques.
Génération de la clé
Chaque fois que tu crées un secret, Vaulted génère une nouvelle clé de chiffrement AES-256-GCM à l'aide de la Web Crypto API :
crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [
'encrypt',
'decrypt',
]);
Quelques points à noter :
- AES-GCM (Galois/Counter Mode) assure à la fois la confidentialité et l'intégrité — il détecte les altérations, pas seulement les lectures non autorisées.
- La longueur de clé de 256 bits est l'option la plus robuste disponible pour AES, utilisée par les gouvernements et les institutions financières du monde entier.
- Extractable :
true— la clé peut être exportée sous forme d'octets bruts afin d'être placée dans le fragment d'URL destiné au destinataire.
La Web Crypto API délègue au moteur cryptographique natif du navigateur (par exemple BoringSSL dans Chromium, NSS dans Firefox). La clé est générée de façon sécurisée en mémoire et ne touche jamais ni le disque ni le réseau.
Chiffrement
Une fois la clé générée, Vaulted chiffre ton texte en clair :
const iv = crypto.getRandomValues(new Uint8Array(12));
crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
new TextEncoder().encode(plaintext),
);
Le vecteur d'initialisation (IV) : AES-GCM exige un IV aléatoire de 12 octets (96 bits) pour chaque opération de chiffrement. L'IV n'a pas besoin d'être secret — il est stocké aux côtés du texte chiffré — mais il ne doit jamais être réutilisé avec la même clé. Comme Vaulted génère une nouvelle clé pour chaque secret, la réutilisation d'un IV est impossible par conception.
Encodage : le texte chiffré et l'IV sont tous deux encodés en chaînes base64url (du base64 standard où + est remplacé par -, / par _, et les = finaux sont supprimés). Cela les rend sûrs à inclure dans des URL et des charges utiles JSON.
Le texte chiffré et l'IV sont envoyés au serveur pour être stockés. Le texte en clair ne quitte jamais le navigateur.
Livraison de la clé par le fragment d'URL
Après le chiffrement, la clé est exportée et placée dans le fragment d'URL — la partie après # :
const rawKey = await crypto.subtle.exportKey('raw', key);
// rawKey → base64url-encoded string
Le lien de partage obtenu ressemble à ceci :
https://vaulted.fyi/s/abc123#dGhpcyBpcyBhIGtleQ
^^^^^^^^^^^^^^^^^^^^^^^^
encryption key (base64url)
Le séparateur # est essentiel. D'après la RFC 3986, l'identifiant de fragment est traité entièrement par le client. Les navigateurs n'incluent jamais le fragment dans les requêtes HTTP — ni dans l'URL, ni dans l'en-tête Referer, ni nulle part où le serveur pourrait le voir.
Quand le destinataire ouvre le lien :
- Le navigateur demande
/s/abc123au serveur (sans le fragment) - Le serveur renvoie le texte chiffré et l'IV
- JavaScript lit le fragment depuis
window.location.hash - La clé est réimportée dans la Web Crypto API
- Le texte chiffré est déchiffré localement
Le serveur assure le stockage et la récupération du blob chiffré. Il ne participe jamais aux opérations cryptographiques.
Protection optionnelle par phrase secrète
Pour les secrets qui nécessitent une protection supplémentaire, Vaulted prend en charge l'encapsulation de clé basée sur une phrase secrète. Cela ajoute une seconde couche : même si quelqu'un intercepte le lien, il ne peut pas déchiffrer le secret sans la phrase secrète.
Dérivation de clé avec PBKDF2
Quand tu définis une phrase secrète, Vaulted dérive une clé d'encapsulation à l'aide de 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 itérations rendent les attaques par force brute coûteuses en calcul.
- Un sel aléatoire de 16 octets garantit que des phrases secrètes identiques produisent des clés dérivées différentes d'un secret à l'autre.
- SHA-256 est la fonction de hachage utilisée à chaque itération de PBKDF2.
- La clé dérivée est une clé AES-KW (AES Key Wrap, RFC 3394), et non une clé AES-GCM. L'encapsulation de clé est une opération distincte du chiffrement.
Encapsuler la clé de chiffrement
La clé AES-KW dérivée encapsule la clé de chiffrement d'origine :
crypto.subtle.wrapKey('raw', encryptionKey, wrappingKey, 'AES-KW');
Le fragment d'URL contient désormais à la fois la clé encapsulée et le sel, séparés par un point :
#wrappedKeyBase64url.saltBase64url
Désencapsulation au déchiffrement
Quand le destinataire saisit la phrase secrète, Vaulted inverse le processus :
- Découper le fragment sur le
.pour obtenir la clé encapsulée et le sel - Dériver la même clé d'encapsulation AES-KW à l'aide de PBKDF2, avec la phrase secrète et le sel
- Désencapsuler la clé de chiffrement avec
crypto.subtle.unwrapKey - Déchiffrer le texte chiffré avec la clé AES-GCM désencapsulée
Si la phrase secrète est incorrecte, unwrapKey lève une erreur — il n'y a ni déchiffrement partiel ni fuite d'information.
Déroulement du déchiffrement
Sans phrase secrète, le déchiffrement est direct :
- Importer la clé depuis le fragment d'URL (
crypto.subtle.importKey) - Déchiffrer le texte chiffré avec l'IV (
crypto.subtle.decrypt) - Décoder le texte en clair à partir des octets UTF-8
Avec une phrase secrète :
- Découper le fragment pour en extraire la clé encapsulée et le sel
- Dériver la clé d'encapsulation à partir de la phrase secrète et du sel (PBKDF2)
- Désencapsuler la clé de chiffrement (AES-KW)
- Déchiffrer le texte chiffré (AES-GCM)
- Décoder le texte en clair
Tout cela se passe dans le navigateur. Le serveur renvoie le blob chiffré et des métadonnées (nombre de consultations, expiration) — rien de plus.
Modèle de menace
Aucun outil de sécurité ne protège contre tout. Voici ce que l'architecture de Vaulted est conçue pour gérer, et ce qui sort de son périmètre.
Ce contre quoi Vaulted protège
- Compromission du serveur — Le serveur ne stocke que du texte chiffré. Sans la clé (qui n'est jamais envoyée au serveur), les données sont illisibles. Un dump complet de la base de données ne fournit que des blobs chiffrés.
- Fuite de la base de données — Comme ci-dessus. Du texte chiffré sans les clés est inexploitable en pratique avec AES-256-GCM.
- Man-in-the-middle côté serveur — La clé de chiffrement vit dans le fragment d'URL, qui n'est jamais transmis dans les requêtes HTTP. TLS protège le texte chiffré en transit.
- Les opérateurs du service lisant les secrets — Le zero-knowledge signifie exactement cela. Nous ne pouvons pas lire tes secrets, même contraints de le faire, parce que nous n'avons pas les clés.
Ce contre quoi Vaulted ne protège PAS
- Navigateur ou appareil compromis — Si l'appareil du destinataire contient un logiciel malveillant ou un enregistreur de frappe, le texte en clair déchiffré peut être capturé après le déchiffrement.
- Extensions de navigateur malveillantes — Les extensions ayant accès à la page peuvent lire le DOM après le déchiffrement, y compris le secret révélé.
- Interception du lien — L'URL contient la clé de chiffrement dans le fragment. Quiconque obtient l'URL complète (fragment inclus) peut déchiffrer le secret. Partage les liens via des canaux sécurisés.
- Regard par-dessus l'épaule — Une fois le secret affiché à l'écran, quiconque regarde peut le voir.
- Comportement du destinataire — Après le déchiffrement, le destinataire peut faire une capture d'écran, copier ou partager le texte en clair. Vaulted ne peut pas l'en empêcher.
La fonction de phrase secrète atténue le risque d'interception du lien : même avec l'URL complète, l'attaquant a encore besoin de la phrase secrète. Partage la phrase secrète via un autre canal (un appel téléphonique, un message séparé) pour une défense en profondeur.
Résumé
Le chiffrement zero-knowledge de Vaulted signifie :
- Une nouvelle clé AES-256-GCM est générée pour chaque secret
- Le chiffrement a lieu entièrement dans ton navigateur
- Le serveur ne stocke que du texte chiffré
- La clé de déchiffrement ne circule que dans le fragment d'URL, que les navigateurs n'envoient jamais aux serveurs
- L'encapsulation optionnelle par phrase secrète ajoute une seconde couche via PBKDF2 et AES-KW
Le résultat : même si chaque serveur, base de données et chemin réseau étaient compromis simultanément, tes secrets resteraient chiffrés. Les clés n'existent que dans les liens que tu partages.
Prêt à partager un secret en toute sécurité ? Crée un secret sur Vaulted — ça prend 10 secondes.
À lire aussi
- La sécurité chez Vaulted — résumé de notre modèle de sécurité
- Pourquoi le chiffrement côté client est important — les arguments en faveur du chiffrement dans le navigateur
- Playground de chiffrement — observe le chiffrement AES-256-GCM étape par étape