Pourquoi les identifiants dans la chaîne de requête fuient avant même d'être lus
Les identifiants dans la chaîne de requête se propagent dans les journaux, l'historique, les proxys et les exports. Utilisez l'injection d'en-tête pour garder les secrets hors des URL et du contexte des agents.

Un secret dans une URL a déjà fait un long détour. Il a peut-être traversé une bibliothèque cliente, un proxy, un journal d'accès, un système de traçage, une base d'historique de navigateur et un export CSV avant même que l'API le reçoive. HTTPS protège cette requête sur le réseau. Il ne force pas toutes les machines et tous les services qui traitent l'URL à l'oublier.
C'est pourquoi un agent autonome peut éviter de voir un jeton tout en provoquant une fuite d'identifiant. Si l'agent demande à une passerelle d'action d'appeler https://api.example.test/v1/builds?access_token=..., la passerelle peut garder le jeton hors de la transcription de l'agent tout en construisant une URL que d'autres systèmes enregistrent habituellement. Le secret a quitté la portée du modèle, mais s'est retrouvé dans un ensemble d'emplacements bien plus large.
La solution est moins spectaculaire que l'analyse de secrets. Réservez l'URL à l'identité de la ressource et aux filtres ordinaires. Placez les identifiants dans l'en-tête de requête, ou dans le corps lorsque le protocole le demande. Puis laissez le composant qui détient le secret l'injecter au tout dernier moment. Vous séparez ainsi une requête que l'on peut nommer sans risque, rejouer dans un test et inclure dans un enregistrement d'audit, d'une requête qui porte une autorisation.
Une URL est un enregistrement, pas seulement une route
Une URL est conçue pour être copiée, affichée, comparée, mise en cache, enregistrée comme favori et journalisée. Ces qualités la rendent utile pour identifier une ressource et très mauvaise pour transporter un jeton bearer. Un paramètre de requête fait partie de la cible de requête, que de nombreuses couches traitent comme une donnée opérationnelle ordinaire.
CWE-598 appelle cette faiblesse « Use of HTTP Request With Sensitive Query String ». Son contexte mentionne les voies de fuite habituelles : historique du navigateur, en-têtes Referer, journaux web et autres sources d'enregistrement. La mesure à prendre est tout aussi simple : envoyez les informations sensibles dans les en-têtes ou le corps de requête. Ce conseil n'interdit pas GET. Il avertit que placer un secret dans l'URI augmente le nombre de personnes et de systèmes capables de le récupérer.
RFC 9110 fait la même distinction dans ses considérations de sécurité. Il avertit que les champs de requête URI construits à partir d'une saisie utilisateur peuvent transporter des données sensibles et qu'un URI différent, généré par le serveur, peut retirer ces données sensibles des liens ultérieurs. Pour les identifiants d'API, allez plus loin : ne créez pas d'URI porteur de secret dès le départ. Le remplacer plus tard laisse des copies.
Un paramètre d'URL est parfois présenté comme « juste une clé API » ou « seulement un jeton de courte durée ». Aucune de ces appellations ne change sa surface d'exposition. Un jeton de courte durée peut encore être valide lorsqu'un collecteur de journaux le transmet. Une clé API peut autoriser un point de terminaison limité, mais ce point peut suffire pour lire des données, générer des coûts ou obtenir un meilleur identifiant. Traitez les données d'autorisation comme un secret jusqu'à ce que le service qui les a émises indique le contraire.
Les journaux d'accès conservent la partie que l'on oublie
La plupart des journaux d'accès HTTP incluent la méthode, la cible de requête, le statut, la taille et la durée, car les opérateurs ont besoin de ces champs pour diagnostiquer le trafic. La cible de requête contient le chemin et la chaîne de requête. Une ligne typique ressemble à ceci :
203.0.113.24 - - [14/Jun/2026:12:42:18 +0000] "GET /v1/builds?access_token=sk_live_example HTTP/1.1" 200 481
La partie dommageable ne provient pas d'un réglage de débogage inhabituel. C'est le champ ordinaire qu'un opérateur recherche lorsqu'une route commence à renvoyer des erreurs. Masquer un en-tête Authorization est courant, car les équipes s'attendent à y trouver des secrets. Masquer des clés de requête arbitraires est plus difficile : un service en amont l'appelle token, un autre utilise api_key, un troisième accepte sig et un quatrième place l'identifiant dans un blob signé.
N'acceptez pas « nous masquons les journaux » comme réponse tant que quelqu'un ne peut pas montrer la cible de requête exacte après masquage, y compris les valeurs de requête, à chaque saut. Une règle qui masque token manque access_token. Une règle qui masque access_token manque un fournisseur qui appelle la même valeur key. Une règle qui masque des noms connus ne fait rien pour une URL pré-signée où l'identifiant est réparti entre plusieurs paramètres.
La différence pratique est la suivante : le masquage des en-têtes protège autour d'une requête porteuse de secret, tandis que l'injection d'en-tête empêche l'URL de porter un secret. Vous avez toujours besoin de contrôles de journalisation pour les en-têtes. Vous pouvez aussi cesser d'écrire une liste sans fin d'exceptions pour des noms de requête, qui finit toujours par prendre du retard sur les API des fournisseurs.
Les proxys inverses transforment une requête en plusieurs enregistrements
Un proxy inverse voit la requête complète avant de la transmettre. C'est aussi le cas des équilibreurs de charge, passerelles API, maillages de services, WAF, services CDN en périphérie et agents d'observabilité qui instrumentent le cycle de vie de la requête. Ils n'enregistrent pas tous le même format, ne conservent pas les données pendant la même durée et ne les envoient pas vers le même compte.
C'est important, car un journal d'application propre ne prouve presque rien. Un journal d'entrée peut contenir l'URL d'origine. Un journal d'erreurs de proxy peut la répéter lorsqu'une connexion en amont échoue. Une trace peut attacher http.target ou une valeur de route. Un paquet de support peut regrouper plusieurs de ces fichiers parce qu'une personne avait besoin d'aide sur un délai d'attente. Chaque copie est raisonnable, prise isolément. Ensemble, elles transforment une clé divulguée en problème d'inventaire.
Les équipes tentent souvent de régler cela avec un filtre de masquage global. Utilisez des filtres, mais connaissez leur limite. Un filtre ne fonctionne qu'après qu'un composant a reçu l'URL, l'a correctement analysée et a reconnu chaque orthographe du secret. Il encourage aussi à garder les identifiants dans les URL, parce que les retirer imposerait de modifier le code client. La correction durable se trouve à la frontière de l'appel, là où l'identifiant rejoint la requête.
Supposons qu'un agent prépare une requête de déploiement. Il peut produire cette description d'action sûre :
GET https://deploy.example.test/v2/releases?project=docs-site&limit=20
credential: deploy-read
Une passerelle qui détient l'identifiant résout deploy-read dans son coffre et envoie Authorization: Bearer ... en amont. Le proxy voit toujours une requête, bien sûr. Sa cible contient project et limit, pas la valeur bearer. Si son journal d'en-têtes expose accidentellement Authorization, c'est un défaut distinct, avec un test précis et évident. Ne cachez pas ce défaut, mais ne le multipliez pas par une fuite d'URL.
L'historique du navigateur est une fuite locale qui persiste longtemps
L'historique du navigateur n'est pas le principal chemin pour un agent sans interface, mais il révèle une erreur dans les flux de travail humains. Les ingénieurs collent une URL défaillante dans un navigateur pour inspecter une page d'erreur, reproduire un rappel ou prouver qu'une route API fonctionne. Le navigateur stocke l'adresse complète, la suggère plus tard et peut synchroniser l'historique selon les réglages locaux. Un enregistrement d'écran, une session partagée ou un collègue qui emprunte la machine peuvent la faire réapparaître.
L'en-tête Referer ajoute une autre voie. Lorsqu'un navigateur charge une page dont l'adresse contient un secret de requête et que cette page demande une ressource ou suit un lien, un serveur en aval peut recevoir une valeur de référent soumise à la politique du navigateur. Les politiques de référent modernes réduisent certains cas, mais elles ne rendent pas une URL secrète saine par conception. L'identifiant ne devrait pas être présent pour que la politique le protège.
C'est pourquoi « l'agent ne l'a jamais vu » ne suffit pas. Un développeur peut voir l'URL dans un résultat d'agent, la coller dans un ticket ou l'utiliser lors d'une vérification manuelle. Tout système qui affiche une URL encourage sa copie. Placez un nom de secret opaque dans l'action visible par l'humain, pas une valeur secrète.
Une exception mérite d'être citée : une URL signée accorde volontairement l'accès par l'URL elle-même. C'est parfois le protocole proposé par un service de stockage pour un téléchargement temporaire. Traitez-la comme une capacité contrainte, pas comme un identifiant d'API ordinaire. Limitez sa durée de validité, restreignez-la à un objet et à une méthode lorsque le fournisseur le permet, évitez de l'afficher et ne laissez pas un agent choisir ses propres paramètres de requête arbitraires. Une URL signée reste sensible dans l'historique et les journaux. Son caractère temporaire limite les dégâts, il ne supprime pas la voie de fuite.
Les exports d'audit transforment un incident en événement de diffusion
Une piste d'audit doit vous aider à savoir qui a demandé une action, quelle identité d'identifiant l'a autorisée, quelle destination l'a reçue et ce qui s'est passé. Elle ne doit pas devenir un second magasin d'identifiants. Le schéma d'audit dangereux enregistre une URL complète parce que cela semble fidèle. Fidèle à quoi ? Il conserve fidèlement une valeur dont l'enquêteur ne devrait jamais avoir besoin.
Utilisez un enregistrement d'audit qui sépare les identifiants stables des éléments secrets. Un enregistrement d'action HTTP utile peut inclure la méthode, le schéma, l'hôte, le chemin, les noms et valeurs de requête non sensibles, l'alias d'identifiant, l'identité de session, la décision, le statut, la classification de réponse et les horodatages. Il peut stocker une empreinte de certains composants de la requête si vous avez besoin de preuve d'altération. Il doit exclure les valeurs d'autorisation, le contenu des cookies et les valeurs de requête secrètes.
Cette forme rend aussi les exports plus sûrs. Les archives JSON, CSV et de support sortent de leur frontière d'accès initiale. Quelqu'un les envoie à un fournisseur, les joint à un bug, les enregistre sur un disque partagé ou les charge dans un tableur. C'est la vie normale d'un export. Concevez-le pour qu'il permette de répondre à une enquête sans devenir une file de rotation d'identifiants.
Sallyport projette les sessions d'agent et les appels individuels depuis un journal d'audit chiffré unique, chaîné par hachage. Sa commande sp audit verify vérifie cette chaîne hors ligne sur du texte chiffré, sans clé. Elle prouve que le journal n'a pas été modifié. Elle n'excuse pas l'enregistrement d'URL secrètes. L'intégrité et la confidentialité résolvent des problèmes différents, et les équipes les confondent régulièrement parce que les deux sont appelés « sécurité de l'audit ».
Un journal inviolable qui contient un identifiant actif peut prouver exactement quand l'identifiant a fuité. Un journal masqué sans mécanisme d'intégrité peut être sûr à partager, mais difficile à croire. Il faut les deux propriétés, appliquées à des champs différents.
L'injection d'en-tête garde le secret hors de la description d'action
L'injection d'un bearer ou d'un en-tête personnalisé fonctionne parce que l'appelant peut décrire la destination sans posséder l'identifiant. La passerelle détient le lien entre une étiquette d'identifiant et son entrée chiffrée dans le coffre. Elle construit la requête, ajoute l'en-tête, l'envoie et renvoie le résultat. L'agent ne reçoit ni la valeur de l'en-tête ni un espace réservé qu'il pourrait développer.
Pour une API bearer, la structure est simple en principe :
agent request
method: GET
url: https://metrics.example.test/v1/usage?team=infra
credential: metrics-production
gateway outbound request
GET /v1/usage?team=infra HTTP/1.1
Host: metrics.example.test
Authorization: Bearer [vault value]
Le texte entre crochets est une notation explicative, pas une valeur qui devrait apparaître dans une transcription réelle. Dans une frontière bien conçue, l'agent ne peut pas demander à l'afficher, l'enregistrer dans un fichier ni le déplacer dans une chaîne de requête. La passerelle traite l'identifiant comme une donnée qu'elle peut utiliser, pas comme une donnée qu'elle peut transmettre.
Les en-têtes personnalisés méritent le même traitement. Certains services utilisent X-API-Key, Api-Key ou un en-tête propre à un fournisseur plutôt que Authorization. Le nom exact change, mais pas la règle : la description de requête visible par le client doit désigner une identité d'identifiant, et la passerelle doit injecter la valeur à l'exécution. L'authentification Basic appartient aussi à l'en-tête, même s'il vaut mieux préférer la méthode plus robuste prise en charge par le fournisseur lorsqu'elle existe.
Sallyport prend en charge l'injection d'identifiants bearer, basic et par en-tête personnalisé pour les appels HTTP. C'est utile ici, car un agent compatible MCP peut demander une action HTTP sans conserver la clé API dans son propre contexte. Cette même frontière n'est pas un filtre magique : l'URL et les champs fournis par l'agent peuvent toujours contenir des secrets si vous les acceptez. Validez la forme de la requête et refusez les valeurs qui ressemblent à des secrets aux endroits qui doivent rester publics.
POST ne corrige pas un identifiant dans l'URL
Passer de GET à POST tout en laissant ?api_key=... dans l'URL ne change presque rien à cette fuite. Les proxys reçoivent toujours la cible de requête. Les journaux d'accès l'enregistrent encore souvent. Les navigateurs et outils peuvent toujours l'afficher. CWE-598 précise explicitement qu'une chaîne de requête peut apparaître avec des méthodes autres que GET.
Déplacer un identifiant dans un corps de requête peut réduire la journalisation accidentelle dans certaines piles, car les journaux d'accès n'incluent généralement pas les corps par défaut. Ce n'est pas la même chose que l'injection d'en-tête. Les corps sont souvent capturés par les middlewares de débogage, les clients API, les rapporteurs d'erreurs et les outils d'enregistrement de requêtes. Ils font aussi de l'identifiant une partie de la charge d'action qu'un agent pourrait être tenté de construire ou de répéter.
Utilisez la méthode et le corps exigés par l'API. Placez un secret dans un corps uniquement lorsque le protocole l'impose explicitement, par exemple lors d'un échange de jeton qui spécifie des paramètres de formulaire. Limitez alors la journalisation des corps, excluez le point de terminaison de la capture large de requêtes et gardez l'échange dans le composant qui détient l'identifiant. Ne transformez pas chaque requête de lecture en POST par superstition. Respectez la sémantique HTTP et retirez le secret de l'URI.
Une mauvaise recommandation connexe consiste à dire « encodez l'URL et les journaux sont inoffensifs ». L'encodage en pourcentage ne change que la représentation. Toute personne ayant l'URL peut la décoder, et de nombreuses visionneuses de journaux le font déjà. Base64 pose le même problème. L'encodage peut rendre une requête plus difficile à repérer à l'œil nu tout en laissant plus facilement passer les règles de masquage.
Une migration sûre commence par des preuves, pas par une modification massive
Ne remplacez pas chaque paramètre de requête. Des paramètres comme page, sort, project et fields sont souvent légitimes, utiles et non secrets. Commencez par trouver où les identifiants entrent réellement dans les URL, puis modifiez ces appels avec un test qui observe la requête sortante.
Suivez cette séquence :
- Recherchez dans le code source, les prompts d'agent, les commandes curl enregistrées, les jeux de tests, les tableaux de bord et les procédures d'exploitation des noms tels que
token,key,secret,signatureetcredential. Recherchez aussi les URL complètes contenant?. Les noms varient. - Collectez des journaux d'accès et des données de traçage représentatifs de chaque couche d'entrée. Vérifiez si chaque cible de requête enregistre les valeurs de requête, et pas seulement les noms de paramètres. Traitez aussi les exports conservés et les paquets de support comme une couche supplémentaire.
- Demandez au propriétaire de l'API quel mécanisme d'en-tête ou de corps elle prend en charge. Si elle n'accepte qu'un identifiant de requête, documentez l'exception, limitez fortement la portée de l'identifiant et isolez cet appel des agents généralistes.
- Faites évoluer l'interface d'action de
url with secretversurl plus credential alias. Ajoutez un test qui refuse une URL contenant un jeton de test connu et vérifie que l'en-tête sortant le contient. - Faites tourner chaque identifiant apparu dans une URL, puis supprimez les anciens journaux et exports selon votre processus de rétention. Une rotation sans nettoyage laisse une exposition historique, un nettoyage sans rotation laisse une clé encore active en circulation.
C'est au niveau du test que beaucoup de migrations échouent. Un test unitaire qui ne vérifie que la réponse HTTP finale ne peut pas dire si la clé est passée dans l'en-tête ou dans la chaîne de requête. Placez un serveur de test local derrière le client et capturez séparément la méthode, le chemin, la requête et les en-têtes. Vérifiez que la requête ne contient pas la valeur de test et que seul l'en-tête voulu la contient.
Pour une passerelle, ajoutez un cas de refus. Donnez-lui https://api.example.test/v1/jobs?access_token=test-canary avec n'importe quel alias d'identifiant et faites-la échouer avant l'appel réseau. Cela détectera un futur modèle de prompt ou un wrapper de confort qui tente de remettre des secrets dans les URL. Un jeton canari doit être unique et inutilisable en dehors du test.
Le masquage reste nécessaire une fois la conception corrigée
L'injection d'en-tête réduit l'ensemble des fuites probables. Elle ne rend pas les journaux sûrs par décret. Une application peut répéter un en-tête d'autorisation dans une exception, un proxy peut journaliser tous les en-têtes pendant un incident de débogage, ou un agent peut coller dans un ticket des données de réponse contenant un secret. Conservez le masquage, le contrôle d'accès, les limites de rétention et la réponse aux incidents.
Mais placez ces contrôles dans le bon ordre. D'abord, gardez les identifiants hors des URL et des descriptions de requête visibles par l'agent. Ensuite, masquez les en-têtes et corps sensibles connus dans chaque journaliseur capable de les capturer. Restreignez ensuite les personnes qui peuvent récupérer les enregistrements bruts et leur durée de conservation. Enfin, entraînez-vous à la rotation et au nettoyage des exports afin que l'équipe puisse agir lorsqu'un contrôle échoue.
Cet ordre évite un piège courant : considérer qu'un modèle de masquage autorise à faire circuler des secrets partout. Les règles de masquage sont fragiles, car elles dépendent des noms, des formats et des analyseurs. Une frontière d'identifiants est plus robuste, car elle contrôle les personnes et systèmes qui reçoivent la valeur.
L'autorisation des agents doit inclure la destination sortante
Un agent qui ne peut pas lire un jeton peut tout de même dépenser son autorité. S'il peut choisir n'importe quelle URL, il peut envoyer un identifiant valide vers un hôte non prévu à cause d'une configuration ambiguë, d'une faute de frappe, d'un chemin SSRF ou d'un prompt qui exploite une interface d'action trop permissive. La non-divulgation du secret et le contrôle de destination sont deux exigences distinctes.
Associez un alias d'identifiant au service prévu et explicitez l'hôte, le schéma et la forme de chemin autorisée. Une passerelle doit refuser un identifiant sélectionné pour metrics.example.test lorsque l'action désigne metrics.example.test.evil.invalid. Elle doit aussi refuser les astuces d'informations utilisateur, les redirections inattendues qui transmettent des identifiants et les URL qui dissimulent un changement d'hôte par encodage. Ces vérifications doivent se faire là où l'en-tête est injecté, car c'est le dernier point qui connaît à la fois l'identité de l'identifiant et la destination analysée.
La carte d'autorisation par session de Sallyport identifie le nouveau processus d'agent par son autorité de signature de code, et les clés par appel peuvent exiger un clic ou Touch ID pour chaque utilisation. Ces contrôles répondent à la question de savoir si ce processus peut invoquer une action. Ils ne rendent pas une destination arbitraire sûre, alors gardez une configuration d'action suffisamment étroite pour qu'une approbation ait un sens concret.
L'artefact utile est un enregistrement de requête partageable
Un test pratique de cette conception est simple : pouvez-vous coller l'enregistrement d'action dans un ticket d'incident sans déclencher une rotation d'identifiants ? Si la réponse est non, l'enregistrement contient trop d'informations.
Visez un enregistrement de ce type :
request_id: 01J...
agent_session: signed-process-42
method: GET
destination: https://metrics.example.test/v1/usage
query: team=infra
credential_alias: metrics-production
authorization: injected, value omitted
result: 200, 481 bytes
Cet enregistrement donne à l'enquêteur assez d'éléments pour corréler l'appel, reproduire la route avec un identifiant de test sûr et demander pourquoi l'agent a choisi metrics-production. Il ne peut volontairement authentifier personne. Si un enquêteur a besoin du secret lui-même, cela relève d'un processus de récupération privilégié distinct, pas d'un champ dans la télémétrie habituelle.
La première action est souvent une recherche assez banale d'URL dans le code source, les prompts et les journaux exportés. Faites-la malgré tout. L'identifiant que vous y trouverez a peut-être été copié par des systèmes dont vous aviez oublié l'existence, et la seule réponse propre est de cesser de créer ce type d'URL.
FAQ
Les identifiants dans la chaîne de requête fuient-ils malgré HTTPS ?
HTTPS chiffre la requête pendant son trajet entre les points de terminaison. Il n'empêche pas les clients, proxys, journaux d'accès, historiques de navigateur ou exports d'audit d'enregistrer l'URL après l'avoir traitée.
Une clé API dans une URL est-elle sûre si elle expire vite ?
Une expiration courte réduit le temps pendant lequel une clé exposée peut être utilisée. Elle n'empêche pas l'URL d'entrer dans les journaux ou l'historique, et un jeton copié peut encore être actif lorsqu'une personne le récupère.
Peut-on toujours enregistrer sans risque les en-têtes Authorization ?
Non. Les valeurs d'en-tête doivent être masquées et protégées par des contrôles d'accès, car les journaux de débogage et les middlewares peuvent les capturer. Les en-têtes sont préférables, car ils gardent le secret hors de la cible de requête et offrent un point de masquage plus prévisible.
Utiliser POST au lieu de GET résout-il les fuites de jetons dans la chaîne de requête ?
Non, si l'identifiant reste après le point d'interrogation. Toute méthode HTTP peut transporter une chaîne de requête, et les proxys et journaux d'accès habituels peuvent toujours la conserver.
Un proxy inverse peut-il retirer des clés API des URL ?
Un proxy peut masquer les journaux ou réécrire une requête, mais il a déjà reçu l'URL d'origine. Placez l'identifiant dans un en-tête avant que la requête atteigne le proxy, dès que l'API en amont le permet.
Que doit recevoir un agent à la place d'un jeton API ?
Donnez à l'agent un alias d'identifiant ainsi que la forme de requête autorisée. La passerelle d'action résout l'alias de façon privée, injecte l'identifiant à l'exécution et renvoie le résultat de l'API.
Faut-il traiter les URL signées comme des secrets ?
Oui. Une URL signée est une capacité volontairement portée par l'URL, elle peut donc fuir par les mêmes traces. Limitez sa durée de vie et sa portée, et évitez de l'afficher ou de l'exporter.
Que doit contenir un enregistrement d'audit HTTP ?
Enregistrez la méthode, la destination, les valeurs de requête non sensibles, l'alias d'identifiant, la décision, le statut et les horodatages. Excluez les valeurs d'autorisation, le contenu des cookies et les valeurs de requête secrètes afin que les exports restent utiles sans devenir des magasins d'identifiants.
Comment vérifier qu'un client utilise l'injection d'en-tête ?
Exécutez le client face à un serveur local de capture et inspectez séparément le chemin, la requête et les en-têtes. Vérifiez qu'un jeton de test connu n'apparaît que dans l'en-tête prévu, jamais dans l'URL ni dans l'enregistrement d'audit généré.
Pourquoi valider la destination si l'agent ne peut pas lire la clé ?
L'agent peut toujours demander une action qui dépense l'autorité de la clé. La passerelle doit associer l'identifiant à l'hôte attendu et aux règles de chemin avant d'injecter l'en-tête.