# Le masquage d'une fiche d'approbation doit préserver la décision

Une fiche d'approbation a un seul rôle : aider une personne à décider si une action précise peut quitter sa machine. Si la fiche masque suffisamment de détails pour protéger un identifiant, mais masque aussi ce que la requête va faire, elle a échoué dans sa mission.

L'erreur habituelle consiste à traiter le masquage comme un simple remplacement de chaînes. Les ingénieurs masquent `Authorization`, vident le corps JSON et déclarent le résultat sûr. L'opérateur voit alors `POST https://api.example.com/...` et un bouton « Approuver ». Ce n'est pas un consentement éclairé. C'est un rituel vide qui habitue les gens à cliquer au moment précis où le contrôle humain devait compter.

Une bonne fiche préserve le sens de l'action tout en supprimant les éléments qui permettraient à la personne qui la lit, la capture, la journalise ou la regarde par-dessus l'épaule de réutiliser un secret. Cela exige un affichage conscient des champs. Les en-têtes, les chaînes de requête, les corps et les identifiants de cible nécessitent chacun un traitement différent.

## Une fiche d'approbation doit expliquer l'action

En quelques secondes, l'opérateur doit pouvoir répondre à quatre questions : qui fait la demande, où va la requête, ce qu'elle va faire et quel objet ou quelle portée elle va affecter. Si l'une des réponses manque, la fiche est incomplète, même si tous les secrets sont parfaitement masqués.

Commencez par une ligne d'action qui associe la méthode du protocole à un verbe compréhensible :

```text
POST  api.billing.example  /v1/invoices/inv_7KD2/refund
Action: issue a refund
```

La méthode compte, car `GET`, `POST`, `PATCH` et `DELETE` n'impliquent pas les mêmes attentes. Le verbe compréhensible compte aussi, car une méthode seule ne dit pas à l'opérateur si `POST /v1/invoices/inv_7KD2/refund` crée un brouillon, soumet un paiement ou déclenche un remboursement. Ne demandez pas à la personne de déduire la sémantique de l'application à partir du nom d'une route lorsque l'appelant connaît déjà l'opération prévue.

Affichez ensuite la destination comme une véritable autorité, et non comme un surnom de compte. « Facturation de production » peut fournir un contexte utile, mais ne peut pas remplacer `api.billing.example`. Une requête mal orientée peut utiliser un libellé familier. L'hôte est la frontière qui indique quel service recevra les données et l'identifiant.

RFC 3986 sépare une URI en plusieurs composants, dont l'autorité, le chemin, la requête et le fragment. Cette distinction est utile pour l'affichage d'une approbation, car chaque composant fournit un signal différent pour la décision. Ne réduisez pas le tout à une jolie chaîne d'URL en espérant que le masquage ultérieur préservera les bonnes informations.

La fiche doit également indiquer si l'action crée, modifie, supprime, publie, transfère ou se contente de lire. « Modifier la fiche client » est moins précis que « changer la destination de versement du client ». Si votre générateur de requêtes ne peut pas fournir cette phrase, corrigez-le. Un moteur d'affichage ne peut pas déduire de manière fiable l'intention métier à partir d'un JSON arbitraire.

## Affichez un manifeste de requête, pas une requête mise en forme

L'unité de travail sûre est un manifeste de requête typé. Il décrit ce que l'agent entend faire avant l'injection des identifiants et avant la création d'un écran d'approbation.

Un manifeste minimal peut ressembler à ceci :

```json
{
  "channel": "http",
  "method": "POST",
  "destination": {
    "scheme": "https",
    "host": "api.billing.example",
    "port": 443,
    "path_template": "/v1/invoices/{invoice}/refund"
  },
  "action": "issue refund",
  "targets": [
    {"role": "invoice", "display": "inv_7KD2", "sensitivity": "internal"}
  ],
  "query": [],
  "headers": [],
  "body": {
    "media_type": "application/json",
    "fields": []
  },
  "effect": "financial"
}
```

Ce n'est pas une représentation HTTP sur le réseau. C'est l'objet que le moteur d'affichage doit consommer. La distinction compte. Une requête réseau contient des identifiants injectés, des valeurs encodées et des détails de transport. Un manifeste porte des libellés sémantiques comme `action`, `target role` et `effect`, absents d'une requête brute.

Classez chaque valeur affichable selon ce dont l'opérateur a besoin pour décider, et non selon l'endroit où elle se trouvait. Un jeton Bearer dans un en-tête est un secret. Une URL de webhook signée dans une chaîne de requête l'est aussi. Une adresse e-mail dans un corps JSON peut être une donnée personnelle. Le nom d'un dépôt peut être un identifiant de cible dont la visibilité est nécessaire pour prendre une décision sûre.

Utilisez un vocabulaire restreint et cohérent :

- `public` : peut être affiché tel quel.
- `internal` : à afficher lorsqu'il identifie l'objet affecté, mais sans le recopier dans des journaux largement accessibles.
- `personal` : afficher seulement la forme minimale utile, généralement un libellé accompagné d'une valeur partielle.
- `secret` : ne jamais afficher la valeur dans la fiche, les journaux, le presse-papiers ou les messages d'erreur.
- `opaque` : afficher un alias approuvé ou une référence stable et non secrète uniquement lorsque cela aide à distinguer la cible.

Ne donnez pas aux appelants une porte de sortie générale du type `safe_to_display: true`. Quelqu'un l'utilisera pour faciliter une session de débogage, puis la laissera dans un chemin qui traite des identifiants de production. Exigez une classification concrète à la frontière où l'agent construit l'action.

## Les en-têtes ont besoin d'un nom, d'un rôle et presque jamais de leur valeur

Les noms d'en-têtes en disent souvent beaucoup plus à l'opérateur que leurs valeurs. Les valeurs contiennent souvent précisément ce qui ne doit pas se retrouver sur une interface destinée aux personnes.

Affichez le nom de chaque en-tête lié à la sécurité et un court libellé indiquant son rôle. Par exemple :

```text
Headers
Authorization: bearer credential from vault
Idempotency-Key: generated request identifier
X-Request-Reason: "refund requested by finance"
Content-Type: application/json
```

La première ligne indique à l'opérateur que la requête sera authentifiée, tandis que la source de l'identifiant lui permet de vérifier que le processus utilise un secret stocké attendu. Afficher `Bearer eyJ...` n'apporte rien à la décision. Cela crée un chemin d'exposition du secret et pousse les gens à comparer des fragments de jeton dépourvus de sens.

La spécification OAuth sur les jetons Bearer définit l'en-tête de requête Authorization comme méthode de transmission privilégiée, tandis que ses recommandations de sécurité traitent les jetons Bearer comme des identifiants à protéger pendant le transport et le stockage. C'est aussi le bon modèle mental pour l'interface d'approbation : l'utilisateur doit savoir qu'un identifiant Bearer est appliqué, pas inspecter cet identifiant.

Suivez ces règles pour les en-têtes :

1. Affichez les valeurs de protocole sûres comme `Content-Type`, `Accept` et `If-Match` lorsqu'elles modifient le comportement.
2. Affichez les noms, mais pas les valeurs, de `Authorization`, `Proxy-Authorization`, `Cookie`, `Set-Cookie`, des en-têtes de signature, des en-têtes de clés API et des en-têtes personnalisés classifiés comme secrets.
3. Affichez une valeur limitée et échappée pour un contexte métier déclaré non secret, comme `X-Request-Reason`, uniquement si elle est courte et ne peut pas contenir de données personnelles ou secrètes.
4. Indiquez qu'un en-tête est absent lorsque cette absence modifie la décision. Un `If-Match` manquant peut compter lors d'une opération d'écrasement.
5. N'affichez jamais tous les en-têtes par défaut. Les bibliothèques de requêtes ajoutent du bruit, et le bruit dissimule l'unique en-tête qui modifie l'action.

Une mauvaise conception courante consiste à masquer un secret avec ses quatre premiers et ses quatre derniers caractères : `sk_live_...9a31`. Cette méthode semble prudente, mais elle est dangereuse pour les valeurs courtes, les valeurs structurées, les clés de test et les valeurs déjà divulguées ailleurs. Elle donne aussi l'impression qu'il faudrait reconnaître des fragments de secrets. Remplacez la valeur par une indication de type comme `identifiant API stocké` ou `signature de requête`.

Les en-têtes peuvent aussi dissimuler des identifiants de cible. Un en-tête de routage vers un locataire, un en-tête d'usurpation d'identité ou `X-Account-ID` peut modifier le destinataire de l'effet. Ne le masquez pas simplement parce qu'il s'agit d'un en-tête. Affichez son rôle et un libellé de cible sûr : `X-Account-ID: compte « Northwind production »`. Si vous ne pouvez pas associer un identifiant opaque à un libellé sûr, indiquez qu'un identifiant de compte opaque sera utilisé et exigez une approbation plus réfléchie pour les opérations sensibles.

## Les chaînes de requête méritent davantage de méfiance

Les chaînes de requête sont visibles dans les URL, copiées dans les terminaux, intégrées aux rapports d'erreur et fréquemment enregistrées par une infrastructure qui ne voit jamais le corps de la requête. Leur côté pratique explique précisément pourquoi les fiches d'approbation doivent les traiter avec soin.

RFC 9110 avertit que les informations d'une URI peuvent être divulguées par des références, des journaux et d'autres canaux, et recommande aux émetteurs d'éviter les informations sensibles dans les URI cibles HTTP. Ce n'est pas une préoccupation théorique liée aux normes. Une fiche qui affiche une chaîne de requête complète peut devenir un canal de divulgation supplémentaire pour une valeur qui n'aurait déjà pas dû se trouver dans l'URI.

Ne considérez pas toutes les valeurs de requête comme sûres parce que la requête est un `GET`. Utilisez les noms, les types déclarés et le contexte de l'opération.

```text
GET  api.crm.example  /v2/contacts
Query
status = "active"
owner = "sales-west"
include = "notes"
access_token = [secret, hidden]
search = [private text, hidden]
```

`status` et `include` sont souvent utiles pour la décision. `search` peut contenir des noms, des adresses e-mail, des termes médicaux ou tout ce qu'un agent a extrait de fichiers locaux. `access_token` est évidemment secret, mais la conception ne peut pas dépendre de noms évidents. Certaines API utilisent `sig`, `token`, `key`, `code`, `state`, `assertion` ou un paramètre propre au fournisseur dont le nom ne donne aucun avertissement.

Traitez les valeurs de requête comme secrètes par défaut, sauf si le manifeste les a explicitement classifiées. Cette règle est volontairement plus stricte que celle de nombreux explorateurs d'API. Une interface d'approbation n'est pas une console de débogage. Son lecteur a besoin de suffisamment d'informations pour autoriser la requête, pas d'une reconstruction octet par octet.

Préservez les paramètres dupliqués et leur ordre lorsqu'ils modifient la sémantique. Un moteur qui transforme une chaîne de requête en dictionnaire peut perdre silencieusement `tag=urgent&tag=finance`, transformer les valeurs répétées ou masquer un problème de signature. Affichez une liste d'entrées plutôt qu'une table associative :

```text
Query
label = "finance"
label = "urgent"
expand = "line_items"
```

Si un champ de requête masqué modifie le routage ou l'autorisation, dites-le. `signature = [signed request value, hidden]` fournit un meilleur signal à l'opérateur qu'une ligne vide. Si la requête contient un lien de partage opaque, n'exposez pas le jeton. Affichez le libellé de la ressource s'il est connu, par exemple `rapport partagé : prévision T2`, et sinon `jeton de ressource partagée présent`.

## Les corps doivent conserver leur structure après le masquage

Un corps réduit à `[redacted]` n'apprend presque rien à l'opérateur. Un corps dont chaque champ est affiché tel quel finira par divulguer quelque chose qui n'aurait jamais dû atteindre l'écran d'approbation. La bonne réponse est un masquage structurel.

Affichez le corps comme un arbre typé. Conservez les clés d'objet, le nombre d'éléments des tableaux, les types de données, les valeurs d'énumération sûres et certains libellés de cible. Remplacez les feuilles dangereuses par un marqueur explicatif.

```json
{
  "invoice": "inv_7KD2",
  "amount": {"currency": "USD", "minor_units": 12500},
  "reason": "duplicate charge",
  "customer_note": "[private text, 84 characters]",
  "payment_method": {
    "id": "[opaque payment method]",
    "token": "[secret, hidden]"
  }
}
```

Cette représentation permet à l'opérateur de voir que l'action rembourse 125,00 USD pour le motif indiqué et qu'une note privée quittera la machine. C'est suffisant pour décider si la requête correspond à la tâche prévue. La fiche ne divulgue ni la note ni le jeton.

Conservez les nombres lorsque les nombres constituent l'effet. Masquer les montants, le nombre de sièges, les périodes de conservation, les limites de débit, les niveaux d'autorisation et le nombre de suppressions rend l'approbation vide de sens. Traitez ces valeurs comme des paramètres d'action et non comme des données accessoires. Un corps `DELETE` contenant `{"purge": true}` doit afficher `purge: true`, faute de quoi la fiche dissimule la partie irréversible.

Le texte exige une règle distincte. Un texte libre peut contenir du code source, des données client, des secrets collés ou des instructions qui modifient l'action. Afficher un aperçu arbitraire est tentant, car cela aide les opérateurs à repérer les absurdités. Mais cela transforme aussi la fenêtre d'approbation en surface d'exfiltration de données. Pour un texte libre non classifié, affichez le nom du champ, le nombre de caractères et le rôle de la destination. N'affichez un extrait limité que si l'appelant marque le champ comme public ou interne et que le moteur échappe les caractères de contrôle.

Les tableaux ont besoin de nombres et de résumés. Voici un mauvais exemple :

```text
recipients: [redacted]
```

Voici une meilleure version :

```text
recipients: 37 email addresses [personal values hidden]
```

Pour une opération destructive, le nombre change la décision. Pour une modification d'accès, affichez le rôle et le nombre : `ajouter 4 membres au rôle : billing-admin`. Si les membres sont des identifiants internes que l'approbateur doit distinguer, affichez des noms ou des alias approuvés, pas les identifiants bruts.

Ne déduisez jamais la sensibilité du seul nom d'un champ. `password`, `token` et `secret` méritent une liste de refus stricte, mais `content`, `message`, `value`, `data` et `metadata` peuvent contenir les mêmes éléments. Le schéma, le générateur d'action ou une annotation explicite du champ doit fournir la classification. Un filtre fondé sur le nom est une dernière ligne de défense, pas la conception principale.

## Les identifiants de cible doivent être lisibles, sans être entièrement exposés

La cible est l'objet qui donne ses conséquences à la requête. Elle peut se trouver dans un segment de chemin, un en-tête, un paramètre de requête, un champ JSON ou un argument de commande SSH. Une fiche doit rendre la cible visible même lorsqu'elle ne peut pas afficher l'identifiant brut en toute sécurité.

Séparez la référence machine d'une cible de sa forme lisible par une personne :

```json
{
  "role": "repository",
  "raw_reference": "repo_01HZX8M9...",
  "display": "payments-service",
  "scope": "production",
  "sensitivity": "internal"
}
```

La référence brute peut être nécessaire à l'exécution, mais c'est la forme lisible qui doit figurer sur la fiche. Si l'action modifie des autorisations, utilisez une phrase qui décrit la relation : `Accorder l'autorisation de déploiement sur payments-service production au compte d'automatisation des mises en production.` La fiche ne doit pas obliger l'approbateur à mémoriser des identifiants opaques.

Parfois, seule la valeur brute identifie la cible. Ne résolvez pas ce problème en l'affichant entièrement. Choisissez une référence stable et non réversible, comme un alias local ou une courte référence d'approbation générée à partir de la valeur protégée. Ne qualifiez pas un identifiant tronqué de hachage, sauf s'il s'agit réellement d'un condensat cryptographique et que vous comprenez les conséquences liées aux collisions et aux corrélations. Dans de nombreux cas, `fiche client [référence opaque 4F8C]` est plus honnête que de prétendre que l'opérateur peut vérifier `cus_Qa8J7kW2m9` en un coup d'œil.

Ne masquez pas excessivement les identifiants qui déterminent le rayon d'impact. Une requête `DELETE /projects/{project}/members` dont le projet est masqué est dangereuse, même si chaque identifiant personnel de membre est masqué. Affichez le nom du projet, l'environnement et le nombre de membres concernés. Gardez les valeurs personnelles sensibles hors de vue.

La distinction est essentielle : masquer un secret protège la confidentialité ; masquer une cible affaiblit l'autorisation. Les équipes confondent souvent les deux sous le terme « masquage ». Ce sont deux tâches différentes, et une fiche doit appliquer des règles différentes à chacune.

## La portée de l'approbation doit correspondre aux informations de la fiche

Une fiche complète n'autorise pas davantage que ce qu'elle décrit. Si une personne a approuvé une requête de lecture d'un dépôt, cette approbation ne peut pas couvrir silencieusement une requête ultérieure qui modifie les paramètres du dépôt simplement parce que les deux viennent du même processus d'agent.

L'approbation par session et l'approbation par appel répondent à des questions différentes. La première demande si ce processus signé peut agir par l'intermédiaire de la passerelle pendant toute la durée de l'exécution. La seconde demande si cette action sortante précise, avec cette cible et cet effet, peut avoir lieu. Les réunir en une immense autorisation donne à la première fiche un poids impossible à porter.

Utilisez une règle d'escalade fondée sur les conséquences. Une lecture depuis un service connu peut entrer dans le cadre d'une autorisation de session. Un appel qui utilise un identifiant spécialement protégé, modifie des accès, envoie un message, crée un engagement financier ou supprime des données nécessite une fiche liée au manifeste de requête concret.

Le résultat de l'approbation doit être lié à un condensat canonique de l'action, et non au texte visible de la fiche. Le condensat doit inclure la méthode, la destination normalisée, les références de cible, les paramètres non secrets classifiés et une représentation des champs protégés. Il doit aussi contenir suffisamment de métadonnées pour détecter une modification de la requête après son affichage. Ne liez pas la décision à un simple résumé adapté aux captures d'écran.

Par exemple, ces deux appels nécessitent des approbations différentes, même si un moteur négligent pourrait les rendre semblables :

```text
POST /v1/roles/grant
body: role = "viewer", subject = "build-bot"

POST /v1/roles/grant
body: role = "owner", subject = "build-bot"
```

Le rôle n'est pas un détail à cacher dans une vue JSON réduite. C'est l'action. Si un ingénieur affirme que la fiche est devenue trop chargée, retirez d'abord les données décoratives du protocole. Ne supprimez pas le champ qui détermine si l'agent peut prendre le contrôle d'un compte.

Sallyport sépare pour cette raison l'autorisation de session des clés par appel. Une décision de session peut identifier et admettre un nouveau processus d'agent, tandis qu'un identifiant marqué pour une approbation à chaque utilisation demande encore une confirmation avant chaque action.

## Un échec de masquage commence généralement avant l'affichage

Prenons un agent chargé d'envoyer un contrat pour signature. Il construit la requête suivante :

```http
POST /v1/envelopes?template=msa&signature=QmFzZTY0U2lnbmVkVmFsdWU HTTP/1.1
Host: api.signing.example
Authorization: Bearer eyJhbGciOi...
Content-Type: application/json

{
  "recipients": [
    {"name": "Maya Chen", "email": "maya@example.com"}
  ],
  "subject": "MSA for Northwind",
  "message": "Please sign the attached agreement.",
  "document": "JVBERi0xLjQK..."
}
```

L'implémentation superficielle met en forme la requête brute, remplace la valeur Authorization et tronque les longues lignes. La fiche expose alors la signature dans la chaîne de requête, l'adresse e-mail du destinataire et peut-être le début d'un document encodé en texte. Le tronquage n'est pas un masquage. Il rend simplement la fuite moins prévisible.

Un manifeste correct sépare d'abord les éléments :

```text
POST api.signing.example /v1/envelopes
Action: send contract for signature
Target: template "msa"
Recipients: 1 email address [personal value hidden]
Subject: "MSA for Northwind"
Message: public text, 39 characters
Document: 1 PDF attachment [content hidden]
Credential: bearer credential from vault
Request signature: present, hidden
```

Cette fiche permet à l'opérateur de repérer un hôte incorrect, un mauvais modèle, un nombre inattendu de destinataires ou un envoi accidentel. Elle n'expose ni l'identifiant, ni la signature, ni l'adresse e-mail, ni les octets du document.

La version dangereuse n'a pas échoué parce que son schéma de masquage n'a pas détecté `signature`. Elle a échoué parce que le système a traité une requête HTTP comme un texte prêt à afficher. Le moteur a reçu un bloc contenant des secrets, sans types de champs, sans rôles de cible et sans savoir quelles valeurs portaient le sens de l'opération.

## Construisez un moteur qui refuse par défaut

Le moteur doit accepter uniquement des entrées structurées, appliquer des règles d'affichage autorisées et refuser d'afficher une action dont certains champs sortants ne sont pas classifiés. Cela semble strict parce que ça l'est. Un champ non classifié est une décision que quelqu'un a repoussée, et le moment de l'approbation est trop tardif pour deviner.

Un contrat d'affichage pratique comporte trois étapes :

1. Normaliser l'action prévue dans un manifeste avant l'injection des identifiants et l'encodage du transport.
2. Vérifier que chaque champ possède un type, un niveau de sensibilité et une règle d'affichage. Refuser les en-têtes, valeurs de requête et feuilles de corps inconnus, sauf si l'appelant les dirige explicitement vers une représentation masquée sûre.
3. Afficher une fiche à mise en page fixe, réservant un espace visible à la destination, à l'action, aux cibles, à l'effet et aux avis concernant les champs protégés.

N'autorisez pas le HTML, les caractères de contrôle du terminal, le Markdown ni les caractères Unicode arbitraires de contrôle de la direction dans les valeurs visibles. Échappez-les avant la mise en page. Une valeur malveillante ne doit pas pouvoir transformer `destinataire : alice@example.com` en ligne trompeuse, créer de faux boutons ou réordonner visuellement un identifiant de cible.

Définissez également des budgets d'affichage. Une chaîne publique peut tout de même comporter 50 000 caractères et rendre la fiche inutilisable. Limitez le texte visible selon le type de champ, indiquez qu'il a été raccourci et ne conservez une vue détaillée contrôlée que pour le contenu déclaré sûr par le manifeste. Ne faites jamais de « afficher la requête complète » une porte de sortie universelle.

Testez le moteur avec des exemples hostiles, pas seulement avec des appels d'API normaux. Incluez un jeton Bearer dans chaque emplacement possible, des noms de requête dupliqués, du JSON contenant des tableaux imbriqués, un segment de chemin avec des délimiteurs encodés en pourcentage, un secret vide, un secret court, un champ de texte très long et une valeur contenant des retours à la ligne. Testez également les requêtes dont le sens dangereux tient à un booléen, un nombre, un rôle ou un hôte de destination.

Enfin, enregistrez ce que l'approbation couvrait sans copier de secrets en clair dans les preuves. Les journaux d'activité et de session de Sallyport proviennent d'un même journal d'audit chiffré et chaîné par hachage, et sa commande de vérification hors ligne peut contrôler la chaîne sans clé du coffre-fort. C'est le niveau à viser : l'auditabilité doit établir ce qui s'est passé sans devenir un second coffre-fort rempli d'identifiants réutilisables.

La fiche doit rendre une mauvaise action visiblement mauvaise. Si une personne peut approuver une requête contenant un identifiant sans voir sa destination, son effet et sa cible, le système a masqué précisément les faits nécessaires à sa protection.
