Quand un administrateur doit transmettre à un collègue un mot de passe de base, une clé API ou un code de récupération, le geste le plus rapide est de le coller dans Slack, Teams ou un e-mail. Le message se cherche. Il se synchronise sur tous les appareils. Six mois plus tard, il est encore là. Passer à un « lien à usage unique » ne suffit pas si l’on écrit encore la clé de déchiffrement en ?key= après l’identifiant du texte chiffré. L’hôte, le reverse proxy et le journal d’accès voient alors la clé. Le chiffrement n’est fait qu’à moitié.

Ce texte n’est pas un mode d’emploi du formulaire autodestructif. Cela appartient à la page outil. La question est plus étroite : quand on envoie un mot de passe une fois, pourquoi la clé de déchiffrement va après # dans l’URL, ce que le serveur ne voit donc plus, et où cette protection s’arrête. Un article précédent montrait comment vérifier dans Réseau que le texte clair n’est pas envoyé. Ici, le même panneau répond à un autre objet : dans quelle tranche de l’URL habite la clé. Le lien à usage unique de MyPassGen suit cette frontière : AES-256-GCM se termine dans le navigateur, le serveur ne garde que le texte chiffré, la clé reste après #, et créer comme lire s’ouvrent sans compte.

Séparer d’abord les deux objets d’une URL

Tout ce qui suit ? est la query. Elle voyage sur la ligne de requête HTTP et atterrit dans les journaux d’accès de l’autre bout. Tout ce qui suit # est le fragment. Le protocole le réserve au client. Placez la clé dans la mauvaise tranche et toute promesse ultérieure de « divulgation nulle de connaissance » s’effondre.

Trois façons d’envoyer le même mot de passe

Prenez un mot de passe de test jetable, orange-lake-7. On peut l’envoyer d’au moins trois manières. La différence n’est pas le slogan de la page. C’est si le texte clair est devenu texte chiffré d’abord, si la clé est entrée dans HTTP, et si l’on peut encore chercher l’original dans six mois.

Méthode Ce que l’hôte ou la messagerie reçoit Ce que l’on retrouve plus tard
Coller le mot de passe dans le chat Le texte clair entier Un original cherchable
Lien chiffré, clé écrite en ?key= Texte chiffré et clé (les deux sur la requête) La clé dans les journaux d’accès ; l’URL complète dans le chat
Lien chiffré, clé après # Le serveur ne voit qu’un identifiant de texte chiffré ; la messagerie peut encore stocker l’URL entière Pas de clé dans les journaux serveur ; le lien complet peut encore rester dans l’historique

La troisième ligne répond seulement à « la machine qui héberge le texte chiffré ne devrait pas recevoir la clé ». Elle ne répond pas à « la messagerie va-t-elle stocker l’URL entière ». Le lien complet est un justificatif : quiconque copie l’adresse avec # peut déchiffrer dans un navigateur. Placer la clé après le dièse règle la confiance envers le serveur. Cela ne règle pas un lien transféré.

Un texte court est la bonne forme pour ce chemin. MyPassGen limite une note à 32 Ko — mots de passe, morceaux de clé et une courte consigne. Un paquet de certificats, un export de tableur ou tout ce qui dépasse ce volume va dans Chiffrer : chiffrer localement en .lock / .enc, puis envoyer la phrase secrète par un autre canal. Le cas fichier est traité dans qui peut lire le texte clair avant un dépôt cloud.

Ce que HTTP transmet vraiment

La RFC 3986 §3.5 nomme la partie après # un identificateur de fragment. Il désigne un emplacement secondaire que le client interprète une fois la ressource principale arrivée. Le navigateur récupère d’abord par chemin et query. Quand le document est là, le fragment décide d’un défilement ou est remis au script de page. La requête HTTP elle-même n’a pas besoin de ce morceau. La page MDN Fragment d’une URI le dit de la même façon : le fragment n’est pas envoyé au serveur ; le client le traite après récupération.

La sémantique HTTP actuelle est plus ferme. La RFC 9110 §7.1 indique que l’URI cible exclut le composant fragment de la référence, parce que le fragment est réservé au traitement côté client. Une origin-form légale sur la ligne de requête est le chemin plus une query optionnelle. Il n’y a pas de production #. Vous pouvez voir s.html?id=abc#la-cle dans la barre d’adresse. La requête document qui quitte l’onglet doit être GET /fr/s.html?id=abc. Le morceau après le dièse reste sur cette machine.

Le Referer retire aussi le fragment. MDN Referer indique que l’en-tête peut porter l’origine, le chemin et la query. Il ne doit pas porter un fragment d’URL ni un nom d’utilisateur et un mot de passe. La Referrer Policy du W3C vide également le fragment dans l’étape de préparation de l’en-tête. Donc « mettre la clé après # et l’hôte qui garde le texte chiffré ne la reçoit pas par HTTP » est un comportement de protocole. Ce n’est pas une promesse de marque.

Le point d’interrogation et le dièse ne sont pas interchangeables

Selon le standard URL WHATWG, quand on ouvre http ou https, la partie après ? entre dans la ligne de requête. Écrivez s.html?id=abc&key=la-cle et la clé apparaît dans les journaux d’accès, les reverse proxies et certains enregistrements CDN. Quels paramètres marketing on peut retirer avant d’envoyer un lien public est un autre problème — voir quels paramètres d’URL supprimer. Une clé de déchiffrement ne doit jamais passer dans la query.

Il existe aussi un piège d’encodage. %23 est l’encodage percent de #. Un %23 écrit dans le chemin ou la query est envoyé au serveur et se décode en caractère dièse littéral. Ce n’est pas un fragment. La clé doit siéger après un # non encodé dans la barre d’adresse, pas après un %23 plié dans le chemin.

Ce qui disparaît des journaux serveur

Un envoi à usage unique se coupe en deux temps. À la création, l’onglet construit une clé aléatoire de 32 octets, chiffre le texte clair avec AES-256-GCM (IV de 12 octets, stocké avec le texte chiffré), puis envoie en POST le texte chiffré, un TTL et un nombre de lectures. À la lecture, le script prend la clé dans location.hash, demande au serveur uniquement le texte chiffré, et déchiffre sur cet appareil. Le lien ressemble à s.html?id={id}#{key} : la query ne porte que l’identifiant ; la clé n’existe qu’après le dièse.

La NIST SP 800-38D spécifie GCM comme chiffrement authentifié : si le texte chiffré est altéré, le déchiffrement doit échouer. On n’obtient pas un corps brouillé que l’on pourrait prendre pour la note. RFC 5116 AEAD_AES_256_GCM utilise une clé de 32 octets, un nonce de 12 octets et une étiquette d’authentification de 16 octets. AesGcmParams sur MDN recommande de même un IV de 96 bits, et il faut un IV neuf à chaque chiffrement sous la même clé. Ces chiffres se vérifient dans une implémentation.

Les journaux serveur doivent donc montrer : l’identifiant du texte chiffré, les champs JSON de création ciphertext / ttl_hours / max_reads, et le GET ultérieur du texte chiffré. Ils ne doivent pas montrer : le texte clair, la clé de 32 octets, ou le morceau après # dans la barre d’adresse. Le TTL peut être 1 heure, 24 heures ou 7 jours, ou la note peut se détruire au seul compteur de lectures. Les lectures vont de 1 à 10. Ce sont des contraintes visibles sur la page de création. Ce n’est pas un engagement de service inventé après coup.

Le protocole ne police pas ce que le script de page rapporte

Que HTTP n’envoie pas le fragment ne veut pas dire que la clé ne quitte jamais cet ordinateur. Un script de page peut lire window.location.hash et le fetch lui-même. Si l’analytics rapporte le location.href complet comme URL de page, le morceau après le dièse entre dans le journal de stats. Lisez la requête analytics dans Réseau. Ne vous arrêtez pas à « HTTP ne l’enverra pas ».

Le lien complet reste un justificatif

Mettre la clé après # ne la cache qu’au service HTTP qui héberge le texte chiffré. Les messageries, les clients mail, l’historique du navigateur et les onglets synchronisés stockent l’URL telle que l’utilisateur l’a vue, dièse compris. Quiconque tient le lien complet peut ouvrir la page de lecture et déchiffrer. C’est une URL au porteur : le lien lui-même est le justificatif. Il n’y a pas de second « mot de passe que seul le destinataire connaît », sauf à en ajouter un à la main.

Pour une remise plus sensible, on peut couper les deux moitiés : un message ne porte que s.html?id=… ; un autre canal — un appel, une conversation de bureau, ou une seconde messagerie — ne porte que le morceau après #. Aucune moitié ne déchiffre seule. C’est un usage, pas le défaut du produit. Le défaut émet encore un lien complet pour que l’autre personne l’ouvre une fois.

Cela n’empêche pas non plus une capture d’écran, une copie ou un transfert du texte clair. Un lien à usage unique réduit les ouvertures répétées et le texte clair durable côté serveur. Si le destinataire photographie la note ou la colle dans un autre canal, le protocole n’y peut rien. Utilisez-le quand vous faites confiance à l’autre personne pour lire une fois et s’arrêter, et quand le secret peut être régénéré et renvoyé. Un mot de passe maître durable, une clé privée ou une phrase de récupération de portefeuille ne devrait pas voyager ainsi.

Contrôler dans l’onglet Réseau

Les étapes ci-dessous ne dépendent d’aucune promesse de marque. Utilisez une phrase de test jetable. Ne vous entraînez pas avec un mot de passe de base encore en service.

  1. Ouvrez les outils de développement, onglet Réseau, et cochez « Conserver le journal ». Ouvrez la page de création à usage unique, saisissez la phrase de test orange-lake-7, réglez les lectures à 1, et générez le lien.
  2. Inspectez le POST de création. Le corps doit avoir un champ de texte chiffré. Il ne doit pas contenir orange-lake-7. Notez l’URL générée : elle doit ressembler à s.html?id=…#…, avec un morceau après le dièse et seulement id après le point d’interrogation.
  3. Collez le lien complet dans un nouvel onglet. La ligne de requête du document doit être …/s.html?id=…. Elle ne doit pas montrer # ni la clé derrière.
  4. Inspectez ensuite les appels Fetch / XHR. La requête de texte chiffré doit porter l’identifiant, pas la clé. L’analytics ne doit emporter ni le morceau après # ni la phrase de test originale.
  5. La page doit déchiffrer la phrase de test. Dans un autre onglet, collez seulement la partie avant le dièse. La page doit indiquer que le lien est incomplet, pas imprimer l’original.
  6. Rechargez le premier lien, déjà lu. Vous devez voir détruit ou expiré, pas l’original. Pour renvoyer, créez un nouveau lien. Il n’existe pas de copie en clair côté serveur.

Le lien à usage unique de MyPassGen travaille dans cette frontière : AES-256-GCM, plafond de 32 Ko de texte clair, clé après #, et aucun compte pour créer ou lire. Ce qu’il faut croire reste Réseau et « retirez le dièse et ça ne déchiffre pas », pas les mots « divulgation nulle » sur la page. La liste complète des contraintes est sur la page Sécurité.

Ce que # ne protège pas

La messagerie professionnelle et certains chats déplient les liens : un backend fait un GET de la page pour attraper un titre ou un extrait. Un aperçu qui récupère le HTML et n’exécute pas le script de page ne voit jamais le fragment et n’appelle en général jamais l’API de texte chiffré. Un scanner qui exécute le script peut terminer une lecture avant le destinataire, et le texte chiffré se détruit alors. Ce n’est pas un trou du protocole. C’est le coût de « ouvrir la page de lecture va chercher le texte chiffré dans le script ». Si l’autre personne dit que le lien a déjà disparu, demandez si un aperçu a gagné la course. Pour renvoyer, créez un nouveau lien.

L’historique du navigateur, les rapports de plantage et certains comptes synchronisés gardent l’URL complète. Laisser un lien à usage unique dans la barre d’adresse, c’est laisser la clé dans l’historique local. Après lecture, ne collez pas l’adresse avec # dans un modèle de ticket, et n’en faites pas un « sauvegarde permanente » dans les favoris. Un lien perdu ne se récupère pas. Il n’existe pas de réinitialisation support, et le pied de page ne publie pas une boîte mail capable de restaurer le texte chiffré.

Quand vous envoyez un texte plus long, le lien et le corps sont un second métier. Retirez utm_source et les identifiants de clic avec le tableau des paramètres ; masquez les numéros de mobile et les NIR dans le ticket avec quels champs masquer avant l’envoi. Un lien à usage unique répond à « l’hôte ne peut pas voir la clé ni le texte clair ». Il ne répond pas à « cette discussion ne devrait pas porter un numéro de compte entier ».

Erreurs fréquentes

« La clé est dans l’URL, donc le serveur la voit forcément. » Cela dépend de la tranche. Après ?, oui. Après #, la RFC 9110 dit que la requête document et le Referer doivent l’omettre. Lisez la ligne de requête dans Réseau. Ne raisonnez pas à l’instinct.

« Si elle est après #, l’historique de chat est sûr. » Les messageries stockent la chaîne que vous avez collée. Une clé absente du journal serveur n’est pas une clé absente pour tout le canal, ni pour qui déverrouillera ce téléphone plus tard.

« Un lien autodestructif bloque les captures d’écran. » Non. Il réduit les ouvertures répétées et le texte clair côté serveur. Il ne réduit pas la copie ni une photo de l’écran. N’envoyez qu’un mot de passe que vous pouvez régénérer.

« Le destinataire doit s’inscrire pour lire. » Sur ce site, créer et lire fonctionnent sans compte. La page de lecture est publique pour le destinataire. « S’ouvre sans connexion » n’est pas « quiconque a le lien doit d’abord se connecter ».

Par où commencer

Commencez par le mot de passe que vous alliez envoyer aujourd’hui. Parcourez d’abord les six étapes ci-dessus avec une phrase de test jetable : la requête de création n’a pas l’original, la ligne de requête de la page de lecture n’a pas la clé après #, retirer le dièse échoue à déchiffrer, et une seconde ouverture montre détruit. Ensuite seulement, envoyez le vrai secret.

Générez le vrai mot de passe sur cette machine avec le générateur. Le mode aléatoire fait 6–128 caractères, 16 par défaut ; en dessous de 8, traitez-le comme faible. Envoyez le lien complet. Pour une remise plus sensible, coupez le dièse et le chemin sur deux canaux. Ne recollez pas le même mot de passe dans le chat comme « sauvegarde ». Après cet envoi, vous pouvez déjà répondre au titre : la clé de déchiffrement va après # pour que le service HTTP qui héberge le texte chiffré ne la voie jamais. Cela ne protège ni l’historique de chat ni une capture d’écran.

Questions fréquentes

Si la clé est après #, le serveur ne la voit vraiment pas ?

Selon la RFC 9110, l’URI cible de la requête document exclut le fragment. Selon MDN, le Referer l’omet aussi. Ouvrez Réseau : la ligne de requête de la page de lecture ne doit pas montrer la clé après le dièse. Si un script de page rapporte location.href de lui-même, c’est une fuite d’implémentation — cherchez-la dans la liste Fetch.

Le destinataire a-t-il besoin d’un compte pour ouvrir le lien ?

Non. Créer et lire fonctionnent sans compte. Le destinataire ouvre la page de lecture et déchiffre. Après le nombre de lectures configuré, ou à l’échéance du TTL, une ouverture suivante montre détruit ou expiré, pas l’original.

Je n’ai copié que la partie avant le dièse. Puis-je encore ouvrir ?

Vous ne pouvez pas déchiffrer. Sans la clé après #, la page de lecture doit indiquer que le lien est incomplet. Demandez l’URL complète à l’expéditeur. Un lien perdu ou déjà détruit ne se récupère pas. Pour renvoyer, créez-en un nouveau.

Est-ce que cela empêche la messagerie de garder une trace ?

Non. Les messageries stockent en général l’URL entière que vous avez collée, clé comprise. Le dièse ne retire la clé qu’au service HTTP qui héberge le texte chiffré. Si vous ne voulez pas qu’un canal durable tienne le justificatif, n’y collez pas le lien complet.