Sécurité

    Pourquoi les URL font fuiter des données sensibles

    Historique, journaux d’accès, CDN et en-têtes Referer gardent l’URL complète, même en HTTPS. Où les liens fuient et comment concevoir des URL sans risque.

    W. Miller · Mis à jour le · À propos de LinkPilot

    Une URL semble privée parce que seules les personnes à qui vous l’envoyez devraient l’avoir. En réalité, l’URL est l’une des données les plus journalisées du web : elle est notée, copiée et transmise par des systèmes qui ne vous ont jamais demandé votre permission et que vous ne pouvez pas auditer. Voici précisément où elle fuit, et comment concevoir des liens qui y résistent. C’est aussi pour cette raison qu’un générateur de liens secrets à usage unique place la clé de déchiffrement dans la partie du lien située après le # : cette partie n’arrive jamais dans le journal d’un serveur.

    La surface de fuite

    • Historique du navigateur. Chaque URL visitée est stockée localement et peut se synchroniser avec d’autres appareils, ainsi qu’avec le service de synchronisation de l’éditeur du navigateur.
    • Journaux d’accès des serveurs HTTP. Par défaut, la plupart des serveurs journalisent le chemin complet de la requête et la chaîne de requête. Toute personne qui a accès aux journaux possède l’URL.
    • Proxys inverses et CDN. Cloudflare, Fastly, votre répartiteur de charge : tous journalisent l’URL, chacun avec sa propre durée de conservation.
    • Chaînes d’agrégation de journaux. Les journaux d’accès restent rarement sur une seule machine : ils sont envoyés vers des services centralisés de journalisation et de supervision, ce qui multiplie le nombre de systèmes qui détiennent l’URL.
    • En-têtes Referer. Quand la page renvoie vers une ressource tierce, le navigateur peut envoyer l’URL d’origine dans l’en-tête Referer. Les scripts d’analyse, les vidéos intégrées et les pixels publicitaires la reçoivent tous.
    • Extensions de navigateur. Les extensions qui ont l’autorisation de lire l’historique de navigation voient chaque URL, y compris la partie après le #.
    • Synchronisation des favoris. Les URL mises en favori se synchronisent sur tous les appareils de l’utilisateur et sur les serveurs de l’éditeur du navigateur.
    • Outils d’aperçu de liens. Slack, Teams, iMessage, LinkedIn et les clients de messagerie récupèrent les URL pour afficher un aperçu, sans rien signaler, dès l’envoi du message.
    • Proxys d’entreprise et passerelles de messagerie. Beaucoup d’organisations inspectent, réécrivent et journalisent chaque URL qui franchit leur périmètre.

    HTTPS garde-t-il l’URL privée ?

    Non. HTTPS chiffre l’URL pendant le transport, et rien d’autre : les deux extrémités la stockent toujours. La précision compte, car « la connexion est chiffrée » est souvent compris comme « l’URL est privée ».

    TLS chiffre le chemin et la chaîne de requête pendant le transport : un observateur placé sur le réseau entre le navigateur et le serveur ne peut pas lire /reset?token=.... Ce que TLS ne masque pas : le nom d’hôte de destination (visible lors de la résolution DNS et dans le champ SNI de TLS sur la plupart des connexions actuelles), ni tout ce qui se passe aux extrémités. Le navigateur enregistre toujours l’URL complète dans l’historique, et le serveur l’écrit toujours dans son journal d’accès. Le chiffrement en transit protège le trajet sur le réseau, pas les deux extrémités, et c’est aux extrémités que les URL fuient.

    Un exemple concret : la vie d’une URL

    Suivons une seule URL sensible, par exemple un lien de réinitialisation, https://example.com/reset?token=8f3a91..., de l’envoi jusqu’au clic :

    1. Le fournisseur de messagerie stocke le message (URL comprise) sans limite de durée, et son scanner de sécurité peut récupérer l’URL à la livraison.
    2. La passerelle de messagerie du destinataire réécrit et journalise l’URL, puis la récupère de nouveau au moment du clic.
    3. Le DNS résout example.com : le nom d’hôte (pas le chemin) figure désormais dans les journaux du résolveur.
    4. Le serveur web écrit la ligne de requête complète dans son journal d’accès. Une entrée au format « combined » par défaut de nginx/Apache ressemble à ceci :
    203.0.113.7 - - [14/Jul/2026:09:12:45 +0000]
      "GET /reset?token=8f3a91... HTTP/1.1" 200 5123 "-" "Mozilla/5.0 ..."
    
    1. Le CDN placé devant lui journalise la même URL, avec sa propre durée de conservation.
    2. Le script d’analyse de la page enregistre l’URL de la page, chaîne de requête comprise, dans un système tiers, sauf s’il est explicitement configuré pour la retirer.
    3. Le navigateur stocke l’URL dans l’historique et la synchronise.

    Un seul envoi, sept copies, détenues par au moins cinq organisations. Aucun attaquant n’est intervenu : c’est le web qui fonctionne comme prévu.

    L’en-tête Referer en pratique

    Si la page qui affiche votre secret renvoie vers une ressource externe (une police servie par un CDN, un script d’analyse, une image intégrée), le navigateur peut envoyer l’URL de la page à cette origine dans l’en-tête Referer.

    Les navigateurs récents appliquent par défaut strict-origin-when-cross-origin (MDN : Referrer-Policy), qui réduit le référent envoyé à une autre origine à la seule origine (https://example.com/ au lieu du chemin complet). C’est un vrai progrès, mais il ne faut pas compter dessus : les requêtes vers la même origine transmettent toujours l’URL complète, une politique définie par la page peut assouplir ce comportement, et les anciens clients et les webviews intégrées ne se comportent pas tous de la même façon.

    Deux parades concrètes :

    <!-- Strip the referer for outbound requests on the page -->
    <meta name="referrer" content="no-referrer" />
    
    Referrer-Policy: no-referrer
    

    L’en-tête s’applique à tout le site, la balise meta à une page précise. Utilisez les deux pour une défense en profondeur. Mieux encore, servez les pages sensibles sans aucune ressource tierce : il n’y a alors personne à qui envoyer un référent.

    Un jeton est-il plus sûr dans le chemin, la chaîne de requête ou le fragment ?

    Seul le fragment reste en dehors de la requête, donc seul le fragment reste en dehors des journaux des serveurs ; le chemin et la chaîne de requête se valent. L’endroit où vous placez un jeton dans l’URL détermine qui le voit :

    EmplacementEnvoyé au serveur ?Dans les journaux du serveur ?Dans le Referer ?Dans l’historique du navigateur ?
    Chemin (/s/abc123)OuiOuiSelon la politiqueOui
    Requête (?token=abc123)OuiOuiSelon la politiqueOui
    Fragment (#abc123)NonNonNonOui

    Le fragment est réellement différent : selon la RFC 3986, section 3.5, il est traité côté client et n’est jamais inclus dans la requête HTTP. C’est pour cela que les outils de partage de texte chiffrés de bout en bout y placent la clé de déchiffrement : le serveur ne la reçoit littéralement jamais. Il n’est pas invisible pour autant : l’historique du navigateur, les extensions et tout JavaScript exécuté sur la page peuvent le lire. Entre le chemin et la chaîne de requête, en revanche, la distinction ne change rien pour la confidentialité : les deux aboutissent dans les mêmes journaux.

    Le problème des jetons de réinitialisation de mot de passe

    Les jetons sensibles dans les URL forment une classe de vulnérabilités reconnue : la CWE-598 de MITRE (« Use of GET Request Method With Sensitive Query Strings ») décrit exactement ce cas. Les données sensibles placées dans la chaîne de requête finissent dans les journaux, les historiques et les référents, quel que soit le chiffrement du transport. C’est pourquoi les parcours de réinitialisation de mot de passe bien conçus ne reposent jamais sur le seul secret de l’URL. Ils rendent le jeton à usage unique, de courte durée et lié à une action ultérieure (la définition d’un nouveau mot de passe), si bien qu’un jeton récupéré dans un journal trois semaines plus tard ne vaut plus rien.

    La leçon vaut au-delà de ce cas : le plus souvent, vous ne pouvez pas tenir une URL à l’écart des journaux, mais vous pouvez faire en sorte que l’URL journalisée ne vaille plus rien au moment où quelqu’un la lit.

    Concevoir des URL qui peuvent fuiter sans danger

    Partez du principe que chaque URL que vous créez finira par apparaître quelque part où elle n’était pas prévue, et faites en sorte que cela n’ait aucune importance :

    • Lecture unique. Détruisez le secret après la première lecture réussie, pour qu’une URL qui a fuité soit déjà inutilisable quand elle apparaît dans l’historique de quelqu’un.
    • Expiration courte. Limitez la fenêtre de fuite. Une URL qui a fonctionné pendant cinq minutes vaut bien moins qu’une URL qui a fonctionné pendant une semaine.
    • Phrase de passe transmise par un autre canal. Exigez un secret qui n’a jamais figuré dans l’URL. Dès lors, l’URL à elle seule, qui est tout ce que contiennent les journaux, ne suffit plus.
    • Séparer la récupération de l’affichage. Servez les métadonnées sur GET et ne livrez le contenu que sur un POST explicite, pour que les outils d’aperçu de liens et les scanners ne puissent pas consommer le secret.
    • Aucune ressource tierce sur les pages sensibles, plus Referrer-Policy: no-referrer et <meta name="robots" content="noindex">.
    • Masquez les routes secrètes dans vos propres journaux. Dans nginx, transformez l’URI de la requête avant la journalisation (voir la documentation de ngx_http_log_module pour log_format) :
    map $request_uri $loggable_uri {
        ~^/s/   "/s/[redacted]";
        default $request_uri;
    }
    log_format redacted '$remote_addr - $remote_user [$time_local] '
                        '"$request_method $loggable_uri" $status';
    access_log /var/log/nginx/access.log redacted;
    

    Vous ne maîtrisez que vos propres journaux : les copies du CDN, du proxy et du navigateur restent. C’est précisément pour cela que les trois premiers points de cette liste font l’essentiel du travail.

    Ce modèle de menace explique le fonctionnement des liens secrets de LinkPilot : l’URL est considérée comme destinée à fuiter tôt ou tard, donc l’URL seule ne suffit jamais. Un GET ne renvoie que des métadonnées ; le contenu exige une action explicite d’affichage par POST, éventuellement protégée par une phrase de passe hachée en SHA-256 côté client. Les secrets détruits, expirés ou révoqués renvoient HTTP 410 et leur contenu est écrasé de façon irréversible, les réponses d’affichage portent Cache-Control: no-store, et les événements d’ouverture journalisent une IP hachée avec un sel renouvelé chaque jour, et non l’adresse brute. Il reste une limite, énoncée franchement sur la page architecture de sécurité : la clé de déchiffrement voyage dans la partie du lien située après le #, donc le lien entier doit être traité comme le secret, ce qui est exactement le risque dont parle cet article. Une expiration courte, la destruction après lecture et une phrase de passe transmise par un autre canal limitent les dégâts quand un lien fuit.

    Mise à jour du 29 septembre 2026 : ce paragraphe indiquait à l’origine que LinkPilot ne proposait pas de chiffrement de bout en bout. Le contenu texte est désormais chiffré dans le navigateur, pièces jointes comprises.

    Essai gratuit, sans inscription

    Message éphémère

    Envoyez une note qui disparaît après une seule lecture.

    Ouvrir l’outil

    Limites

    La conception des URL ne règle pas tout. Elle ne contrôle pas ce que fait le destinataire après une lecture légitime, elle ne peut pas effacer les copies que des tiers détiennent déjà, et elle ne sauvera pas une URL qui expose des données personnelles en clair : si l’adresse e-mail d’un utilisateur figure dans la chaîne de requête, aucune expiration n’empêchera qu’elle soit journalisée. La règle durable : ne placez jamais dans une URL une donnée que vous devriez supprimer plus tard, car supprimer une URL de tous les endroits où elle a été copiée est pratiquement impossible. Concevez plutôt des URL qui ne valent rien une fois qu’elles ont fuité.

    Références

    Outils pour cet article

    Questions fréquentes

    LinkPilot envoie un lien secret qui disparaît une fois ouvert.

    Chiffré dans votre navigateur, affiché une seule fois, puis détruit. Sans compte.

    Créer un lien secret

    À lire ensuite