8 min de lecture

Confusion de type de contenu dans les appels d'API d'agents authentifiés

La confusion de type de contenu peut permettre à des appels d'API d'agents authentifiés de contourner l'intention. Testez JSON, les formulaires, multipart et les corps vides avec un même schéma.

Confusion de type de contenu dans les appels d'API d'agents authentifiés

Les appels authentifiés d'un agent doivent être interprétés de la même manière entre la périphérie réseau et le gestionnaire d'action. Si la passerelle voit une requête JSON inoffensive, que la couche d'autorisation voit un ensemble de champs et que le gestionnaire voit une soumission de formulaire privilégiée, les identifiants ont bien joué leur rôle, mais l'API a tout de même échoué.

Ce problème ne concerne pas uniquement les anciens formulaires de navigateur. Les agents génèrent directement des requêtes HTTP, réessaient souvent, réutilisent des exemples provenant des descriptions d'outils et disposent fréquemment d'identifiants capables de modifier des systèmes réels. Le corps d'une requête fait partie de la décision d'autorisation dès qu'il choisit une cible, un montant, un environnement, une commande ou une permission. Son type de média, sa syntaxe et son schéma doivent être dépourvus d'ambiguïté avant de décider si l'appelant peut agir.

Une requête authentifiée doit toujours avoir un seul sens

L'authentification répond à la question de savoir qui a présenté un identifiant. L'autorisation détermine si cet appelant peut effectuer une action. Aucune de ces réponses ne garantit que tous les composants se sont accordés sur les arguments de l'action.

Prenons un point d'accès qui modifie une cible de déploiement :

POST /v1/deployments/promote HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json

{"environment":"staging","release":"2026.07.22"}

Le code d'autorisation peut autoriser une promotion vers staging, mais refuser la production. Ce code n'est fiable que s'il reçoit la même valeur environment que celle utilisée par le gestionnaire d'action. Si une couche intermédiaire lit le JSON, qu'un gestionnaire consulte ensuite les paramètres de formulaire et que les deux peuvent alimenter un même objet de requête, vous avez créé deux sources de vérité.

L'échec ne nécessite pas de jeton cryptographique défectueux. Un agent disposant d'une session légitime peut envoyer un corps ignoré par une couche et pris en compte par une autre. Un agent compromis peut faire de même. Le résultat est un contournement de l'autorisation exprimé par le formatage de l'entrée.

La RFC 9110 indique que Content-Type précise le type de média de la représentation associée, ainsi que le format des données et la manière dont le destinataire doit les traiter. Cet en-tête fait donc partie de la sémantique de la requête, ce n'est pas une simple décoration. La même RFC autorise aussi un destinataire à supposer octet-stream ou à inspecter les données en l'absence de Content-Type. C'est utile pour la gestion générique de fichiers, mais c'est un mauvais comportement par défaut pour les API d'actions protégées.

Pour un point d'accès d'action, établissez l'invariant suivant :

Un seul type de média accepté transforme les octets de la requête en un seul objet de commande validé. Toutes les décisions de sécurité et tous les effets de bord utilisent cet objet.

Le point d'accès peut prendre en charge plusieurs représentations, mais chaque représentation doit avoir son propre contrat et sa propre suite de tests. Ne considérez pas plusieurs parseurs comme de simples solutions interchangeables.

Un en-tête Content-Type n'est pas un schéma

Content-Type: application/json ne signifie pas « voici la structure de requête attendue ». Cela signifie que l'expéditeur affirme que le corps utilise un type de média JSON. Vous devez encore déterminer si ce type est pris en charge par cette route, si des paramètres sont autorisés, si le corps est syntaxiquement valide et si la valeur décodée respecte le contrat de l'opération.

Un point d'accès protégé doit limiter volontairement l'ensemble des représentations autorisées. De nombreux points d'accès de commande devraient accepter uniquement JSON. Un point d'accès de téléversement peut accepter uniquement multipart. Une action sans argument ne devrait accepter aucun corps. Plus l'ensemble accepté est large, plus vous devez gérer de chemins de parsing.

L'OWASP REST Security Cheat Sheet formule clairement la recommandation pratique : documenter les types de contenu pris en charge et refuser les types inattendus ou absents, tout en autorisant l'omission du type de contenu pour une requête dont la longueur est nulle. Elle avertit aussi que le corps et son type déclaré doivent correspondre afin d'éviter une mauvaise interprétation entre le producteur et le consommateur.

Cette recommandation doit être nuancée pour les actions authentifiées. Ne faites pas correspondre un type déclaré en inspectant le premier caractère et en choisissant un parseur. Un corps qui commence par { ne donne pas le droit de traiter en JSON une requête déclarée comme donnée de formulaire. La détection automatique transforme un contrat clair en supposition d'implémentation.

Un contrat de route utile ressemble à ceci :

RouteType de média autoriséRègle du corps
POST /v1/deployments/promoteapplication/jsonObjet JSON obligatoire correspondant à PromoteRequest
POST /v1/artifactsmultipart/form-dataParties obligatoires correspondant à ArtifactUpload
POST /v1/sessions/revokeaucunDoit contenir zéro octet

Soyez précis sur les paramètres du type de média. Si votre parseur JSON accepte application/json; charset=utf-8, documentez-le et normalisez les paramètres avec une seule bibliothèque. S'il accepte uniquement application/json sans paramètre, refusez le paramètre au lieu de laisser le proxy et l'application appliquer des règles différentes. Le choix compte moins que son application uniforme.

Séparez également la préférence de réponse Accept du Content-Type de la requête. Un client peut demander une réponse JSON tout en envoyant un corps de requête invalide. Ne laissez jamais un en-tête Accept élargir les formats de requête qu'un point d'accès d'action peut analyser.

JSON nécessite des règles qui dépassent la syntaxe valide

Un parseur JSON peut analyser correctement une entrée que votre API doit malgré tout refuser. Les noms de membres en double en sont l'exemple évident :

{"environment":"staging","environment":"production","release":"2026.07.22"}

La RFC 8259 indique que les noms d'objet devraient être uniques et explique pourquoi : les destinataires ne traitent pas les noms en double de la même manière. Beaucoup conservent la dernière valeur, certains échouent et d'autres exposent chaque paire. Il s'agit d'un problème d'interopérabilité documenté, pas d'une préférence théorique de style.

Supposons qu'un middleware d'autorisation utilise un parseur qui conserve la première valeur environment, tandis qu'un décodeur en aval conserve la dernière. Le middleware approuve staging et le gestionnaire déploie production. Vous ne réparerez pas ce problème avec de meilleurs noms de rôles ou une autre revendication de jeton. Refusez la requête avant que l'un ou l'autre composant ne prenne une décision.

Appliquez la même rigueur aux valeurs qui semblent inoffensives dans un langage à typage souple :

  • Refusez les membres d'objet inconnus pour les requêtes d'action, sauf raison de compatibilité clairement établie.
  • Exigez le type JSON attendu. Un booléen n'est pas une chaîne qui contient true, et un identifiant entier n'est pas un nombre à virgule flottante.
  • Définissez une taille maximale du corps avant le parsing. Un validateur de schéma ne peut pas protéger une mémoire déjà épuisée par la lecture d'un corps gigantesque.
  • Décidez si un champ peut être omis, prendre la valeur null ou être une chaîne vide. Ce sont trois états différents.
  • Refusez les données après la fin du document et les extensions du parseur comme les commentaires, NaN ou les noms non cités si votre bibliothèque les propose.

N'autorisez pas directement à partir d'une map générique. Décodez vers un type de requête doté d'un schéma explicite, effectuez la validation sémantique, puis construisez un type de commande interne qui ne conserve pas les artefacts bruts du parseur. Un gestionnaire qui reçoit PromoteCommand { environment, release } a moins de possibilités de réinterpréter l'entrée qu'un gestionnaire recevant une map, une collection de paramètres, un objet de requête et le corps brut.

Les nombres méritent une attention particulière. La grammaire JSON autorise les littéraux numériques volumineux, mais beaucoup d'environnements les décodent en représentation à virgule flottante si vous ne les configurez pas autrement. Si une valeur désigne de l'argent, des quotas, des enregistrements de base de données ou une charge utile signée, utilisez un format texte ou un parseur d'entiers avec une plage documentée. Ne laissez pas une couche arrondir un nombre avant qu'une autre ne le compare.

Les corps de formulaire créent des règles cachées pour les tableaux et l'imbrication

application/x-www-form-urlencoded semble simple parce que ce format ressemble à une chaîne de requête. Il cesse de l'être lorsque les bibliothèques attribuent un sens aux noms répétés, à la notation entre crochets, aux signes plus et aux valeurs vides.

Prenez ces corps :

role=user&role=admin
role[]=user&role[]=admin
role[user]=1&role[admin]=1
role=user%26role%3Dadmin

Selon les frameworks, ces données peuvent devenir un scalaire final ou initial, un tableau, un objet, des noms de champs littéraux ou une erreur de parsing. Certains middlewares analysent les formulaires pour toutes les méthodes HTTP. Certains frameworks fusionnent les paramètres de requête et ceux du formulaire dans un objet pratique. C'est dans cet objet pratique que les API protégées perdent la trace de ce que l'appelant a réellement envoyé.

Les recommandations de test de l'OWASP concernant la pollution des paramètres HTTP indiquent que le comportement dépend des interactions entre l'application, le serveur Web, le WAF et le middleware. C'est précisément pourquoi il faut tester les paramètres répétés bruts au lieu de se fier à la documentation du parseur d'un seul framework.

La recommandation courante d'accepter JSON et les formulaires URL-encodés sur chaque point d'accès « pour assurer la compatibilité client » est généralement mauvaise. Elle subsiste parce qu'elle simplifie l'écriture d'un client de démonstration et parce que de nombreux frameworks l'activent par défaut. Elle double aussi les contrats de représentation de chaque action, puis en ajoute discrètement un troisième lorsque les champs de requête sont fusionnés avec le corps.

Si vous devez prendre en charge un point d'accès utilisant un formulaire, donnez-lui une politique de parsing propre à la route :

  1. Refusez les noms répétés, sauf si le schéma définit le champ comme une liste.
  2. Refusez la syntaxe entre crochets, sauf si le schéma définit son encodage exact et si votre parseur l'implémente uniformément.
  3. Gardez les paramètres de requête séparés des champs du formulaire. N'autorisez aucune source à écraser l'autre.
  4. Convertissez les champs analysés vers la même commande interne typée que celle utilisée par la route JSON, uniquement après validation.
  5. Testez l'encodage en pourcentage, + contre %20, les valeurs vides, l'absence de = et les champs en double à travers le chemin de requête de production.

Ne résolvez pas ce problème en choisissant « le premier gagne » ou « le dernier gagne ». Cela produit une réponse déterministe dans un composant tout en conservant le désaccord ailleurs. Un champ scalaire protégé ne doit apparaître qu'une seule fois.

Multipart est un protocole de téléversement, pas un JSON flexible

Bloquez les appels au niveau du coffre
Le coffre reste verrouillé derrière Secure Enclave et Touch ID. Chaque action est refusée tant que vous ne l'avez pas déverrouillé.

multipart/form-data a une fonction légitime : transporter plusieurs parties dotées chacune de leurs propres en-têtes, souvent avec du contenu de fichier. La RFC 7578 le définit pour les valeurs de formulaire et exige un paramètre boundary qui sépare les parties. Chaque partie peut aussi apporter ses propres en-têtes et métadonnées de nom de fichier.

Cette structure fait de multipart une mauvaise représentation de remplacement pour les commandes authentifiées ordinaires. Elle ajoute de la syntaxe, davantage de gestion de taille, plus d'endroits où des noms de champs peuvent être répétés et plus de possibilités pour qu'une passerelle inspecte une partie tandis que l'application en choisit une autre.

Une mauvaise conception courante accepte une partie JSON metadata avec un fichier, tout en acceptant des champs de formulaire de premier niveau capables d'écraser les métadonnées :

Content-Disposition: form-data; name="metadata"

{"project":"alpha","visibility":"private"}

Content-Disposition: form-data; name="visibility"

public

Un composant peut autoriser la requête en fonction de metadata.visibility, tandis qu'un autre lie la partie de formulaire suivante au paramètre visibility du gestionnaire. La requête possède deux valeurs pour une même propriété sensible, exprimées dans deux grammaires.

Concevez les points d'accès multipart autour de parties nommées, chacune ayant un rôle distinct. Par exemple, acceptez exactement une partie file et exactement une partie manifest. Exigez que manifest soit du JSON doté de son propre schéma strict. Refusez tout nom de partie absent du contrat de téléversement, refusez les parties singleton en double, imposez des limites distinctes pour la taille totale du corps et celle du fichier, et décidez si les valeurs Content-Type au niveau des parties sont obligatoires.

Ne faites pas confiance à un nom de fichier comme chemin, à une déclaration MIME comme classification de fichier ou au comportement d'un parseur multipart pour ses fichiers temporaires comme mesure de sécurité. Ce sont d'autres problèmes propres aux téléversements. La règle concernant la confusion du parseur reste simple : les entrées d'autorisation doivent provenir d'une seule source nommée et validée. Si manifest.project détermine où placer un fichier, aucune autre partie, aucun paramètre de requête et aucun en-tête ne doit pouvoir modifier ce projet.

Lorsqu'une commande ne contient aucun fichier, n'acceptez pas multipart. Chaque type de média supplémentaire offre une nouvelle occasion à deux composants de ne pas s'accorder.

Un corps vide est un contrat, pas une absence de validation

Certaines actions authentifiées n'ont besoin d'aucun argument. Révoquer la session actuelle, renouveler un nonce généré par le serveur ou accuser réception d'un événement fixe peut se faire avec un corps vide. Dans ces cas, rendez l'absence de contenu vérifiable.

Un point d'accès sans corps doit refuser tout ce qui suit :

POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/json
Content-Length: 2

{}
POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

scope=other
POST /v1/sessions/revoke HTTP/1.1
Transfer-Encoding: chunked

0

Le dernier exemple ne contient aucun contenu, mais utilise tout de même un mécanisme de délimitation que votre contrat sans corps peut interdire. Le rejet dépend de votre pile HTTP, mais décidez et testez ce comportement à la périphérie. Ne laissez pas un proxy transmettre une délimitation que l'application interprète autrement.

Pour une route sans corps, appliquez ces règles avant la logique métier :

  • La requête ne contient aucun octet de contenu.
  • La route n'accepte pas Content-Type, sauf si une règle de compatibilité l'autorise explicitement.
  • La route ne fusionne pas les paramètres de requête dans la commande, sauf si chaque nom autorisé apparaît dans son propre schéma.
  • Le serveur enregistre l'action comme dépourvue d'arguments, plutôt que de journaliser un objet de requête générique que des lecteurs ultérieurs pourraient prendre pour une entrée.

La RFC 9110 décrit le contenu des requêtes selon la sémantique de la méthode. Elle ne donne pas un sens universel à un corps simplement parce que la requête utilise POST. C'est le contrat de votre ressource qui lui donne ce sens.

Le cas difficile est celui d'une bibliothèque cliente qui envoie toujours {}. N'élargissez pas le point d'accès pour vous y adapter. Corrigez le client ou donnez-lui une route distincte et documentée. Un corps actuellement sans effet devient souvent un canal d'entrée accidentel après une modification ultérieure du gestionnaire.

Validez avant l'autorisation et exécutez à partir de la commande validée

Conservez une trace de chaque appel
Consultez les appels HTTP individuels dans le journal Activity après l'action de l'agent.

Le pipeline de requête le plus sûr suit une seule direction. Les octets bruts entrent. Une route sélectionne un parseur autorisé. Le parseur produit une valeur typée. La validation produit une commande canonique. L'autorisation évalue cette commande. L'exécuteur reçoit la même commande.

raw HTTP request
  -> route and media-type check
  -> bounded body read
  -> one strict parser
  -> schema and semantic validation
  -> canonical command
  -> authorization
  -> execution and audit record

N'inversez pas les deux étapes centrales. L'autorisation a souvent besoin de champs comme l'identifiant du projet, l'environnement, le destinataire ou le mode de commande. Les équipes sont donc tentées d'inspecter tôt une entrée analysée de manière permissive. Cela crée un parseur préalable à l'autorisation dont le comportement devrait rester identique à celui du décodeur final pour toujours. Peu de systèmes tiennent cette promesse.

La commande canonique est une frontière pratique, pas un motif réservé aux diagrammes. Elle ne doit contenir que les valeurs nécessaires à l'exécuteur et exclure le texte brut du corps, les collections de formulaires, les objets de requête du framework et les alias. Si votre exécuteur reçoit target_environment, il ne doit pas consulter ensuite req.query.environment parce que la cible manquait ou était moins pratique à utiliser.

Cette approche améliore aussi les journaux d'audit. Enregistrez le principal authentifié, le point d'accès, le type de média accepté, une empreinte de la requête, les champs sûrs de la commande canonique à conserver, la décision d'autorisation et le résultat. Journaliser les corps bruts par défaut crée un second problème, car ils peuvent contenir des identifiants, des fichiers téléversés et des données utilisateur. Une empreinte permet de relier un événement à des éléments conservés sans transformer les journaux en coffre de secrets.

La signature des requêtes exige la même rigueur. Si un client signe des octets tandis que le serveur autorise un objet normalisé, documentez les règles de représentation signée et de canonicalisation. Si le client signe un objet canonique, refusez tous les encodages alternatifs avant de vérifier la signature. Sinon, deux séquences d'octets peuvent porter la même requête métier, ou une séquence peut prendre un autre sens après le parsing.

Testez les désaccords, pas seulement le parseur dans le cas nominal

Des tests unitaires qui désérialisent une seule fixture JSON valide ne prouvent presque rien sur l'accord entre les parseurs. Votre cible de test est le chemin de requête public : répartiteur de charge ou proxy inverse, passerelle, middleware du framework, gestionnaire de route et tout service qui analyse à nouveau le corps.

Construisez un corpus compact de cas négatifs pour chaque opération authentifiée. Exécutez-le en CI dans un environnement éphémère et vérifiez la réponse ainsi que l'absence d'effets de bord. Une réponse 400 ne suffit pas si un message de file d'attente, un événement d'audit ou une écriture partielle de fichier a déjà eu lieu.

Commencez avec ce script shell. Il envoie volontairement des corps bruts au lieu de dépendre d'un client généré qui refuse les entrées malformées :

base=https://api.test.example
bearer='test-token'

send() {
  name=$1
  type=$2
  body=$3
  code=$(curl -sS -o "/tmp/${name}.out" -w '%{http_code}' \
    -X POST "$base/v1/deployments/promote" \
    -H "Authorization: Bearer $bearer" \
    -H "Content-Type: $type" \
    --data-binary "$body")
  printf '%-28s %s\n' "$name" "$code"
}

send valid_json 'application/json' \
  '{"environment":"staging","release":"2026.07.22"}'
send duplicate_json 'application/json' \
  '{"environment":"staging","environment":"production","release":"2026.07.22"}'
send form_body 'application/x-www-form-urlencoded' \
  'environment=production&release=2026.07.22'
send false_json 'application/json' \
  'environment=production&release=2026.07.22'

La forme attendue devrait être un succès et trois refus côté client :

valid_json                   200
 duplicate_json               400
form_body                    415
false_json                   400

Votre convention de statuts peut renvoyer 422 pour une requête syntaxiquement valide qui échoue au schéma. Préservez la distinction importante : un type de média incorrect ne doit jamais atteindre un parseur de remplacement et un membre JSON en double ne doit jamais atteindre l'autorisation.

Étendez ce corpus avec des cas ciblant les frontières entre composants :

CasComportement attendu
Content-Type absent avec un corps non videRefuser avant le parsing
Objet JSON avec un champ inconnuRefuser ou appliquer un comportement de compatibilité documenté
Scalaire de formulaire répétéRefuser
Valeur de requête en conflit avec une valeur JSONRefuser ou ignorer la requête selon le contrat de la route
Multipart contient deux parties manifestRefuser
Une route sans corps reçoit {}Refuser

Inspectez ensuite la piste d'audit. Chaque entrée refusée doit posséder une trace indiquant la route et la catégorie du rejet, sans journaliser le contenu sensible de la requête. Chaque entrée acceptée doit produire une commande canonique unique. Si les journaux montrent que la passerelle a vu une cible et que le gestionnaire en a enregistré une autre, vous avez trouvé un désaccord, même si le test a obtenu une réponse 2xx.

Les proxies et les middlewares sont aussi des parseurs

Séparez l'intention des identifiants
Sallyport exécute les actions HTTP depuis son coffre chiffré et ne renvoie à l'agent que le résultat.

Les équipes désignent souvent le parseur de l'application et oublient les composants qui le précèdent. Les proxies inverses peuvent normaliser les en-têtes. Les passerelles API peuvent inspecter le JSON pour appliquer une règle. Les WAF peuvent analyser les données de formulaire. Les middlewares d'observabilité peuvent lire et reconstruire un corps. Un framework peut remplir les champs de requête, de formulaire et JSON avant l'exécution du gestionnaire de route.

Les recommandations de l'OWASP sur le request smuggling HTTP décrivent la version plus large du problème : les intermédiaires et les serveurs en aval peuvent interpréter différemment les limites d'une requête, notamment lors de la traduction de protocole et de la délimitation. La confusion de type de contenu n'a pas besoin du request smuggling pour être dangereuse, mais les deux problèmes viennent du fait que différentes couches prennent des décisions de parsing incompatibles.

Inventoriez chaque lecteur de corps dans le chemin d'action. Pour chacun, notez les types de média analysés, la conservation ou non des valeurs en double, la décompression éventuelle, la limite de taille et la possibilité de réécrire le corps. Si personne ne peut répondre à ces questions, le point d'accès n'est pas prêt à recevoir des identifiants d'agent.

Gardez le rôle de la passerelle limité. Elle peut appliquer les limites de corps d'une route et bloquer les types de média qu'une route n'accepte jamais. Elle peut aussi refuser les en-têtes malformés avant l'application. Mais n'utilisez pas une transformation de passerelle pour convertir un formulaire en JSON ou pour « nettoyer » des champs en double. L'application doit encore refuser l'ambiguïté selon la sémantique exacte qu'elle exécutera.

Testez les versions HTTP et les chemins de déploiement réellement utilisés en production. Une requête qui se comporte correctement contre un serveur local de développement peut changer lorsqu'un client HTTP/2 atteint un proxy qui transmet en HTTP/1.1 à l'application. Le but n'est pas de construire un laboratoire de recherche sur les attaques. Il s'agit de faire prouver à la chaîne de production qu'elle produit un seul objet de commande pour chaque requête acceptée.

Les passerelles d'agents doivent préserver cette frontière

Une passerelle d'agent doit garder les identifiants hors de portée du modèle et conserver une trace de l'action, mais elle ne peut pas rendre sûre à elle seule une API cible permissive. La passerelle doit envoyer une représentation explicitement prise en charge par la route cible, et la cible doit valider cette représentation avant d'évaluer l'autorité.

Le canal HTTP de Sallyport injecte les identifiants tout en gardant les clés API hors de l'agent. Celui-ci peut donc demander une action sans recevoir le secret lui-même. C'est une frontière utile entre l'agent et les identifiants. Associez-la à des contrats de point d'accès qui refusent les corps ambigus, car des identifiants protégés autorisent toujours la requête qui atteint l'API.

Donnez aux agents des outils qui reflètent le contrat au lieu d'exposer, pour les systèmes sensibles, une action générique du type « effectuer n'importe quelle requête HTTP ». Un outil de promotion devrait accepter des arguments typés environment et release. Son implémentation devrait sérialiser un seul objet JSON, définir un seul type de média et refuser les entrées d'outil qui ne peuvent pas respecter le schéma de l'API. Le service récepteur doit répéter la validation. Les schémas d'outils réduisent les erreurs, mais ne remplacent pas la méfiance côté serveur.

Lorsqu'un agent doit téléverser un fichier, faites-en un outil distinct avec un fichier nommé et un manifeste nommé. Lorsqu'il doit effectuer une action sans argument, ne lui fournissez aucun champ de corps. Ces petites contraintes facilitent l'inspection, l'approbation, la relecture dans un environnement de test et l'audit ultérieur de la requête souhaitée par l'agent.

N'approuvez pas une capacité vague en espérant que les parseurs apporteront la précision manquante. Faites en sorte que le point d'accès n'accepte qu'un seul sens, que l'agent envoie ce sens et que toute autre écriture soit refusée avant qu'un identifiant puisse l'autoriser.

FAQ

Qu'est-ce que la confusion de type de contenu dans une API ?

Il s'agit d'un désaccord entre plusieurs composants sur le sens d'une requête HTTP authentifiée. La passerelle, le validateur de schéma, le parseur du framework, le contrôle d'autorisation et le gestionnaire peuvent interpréter différemment les mêmes octets. Une requête peut alors franchir un contrôle sous une certaine interprétation, puis être exécutée sous une autre.

Pourquoi la confusion de type de contenu est-elle risquée pour les agents IA ?

Le risque est plus marqué avec les agents, car ils peuvent envoyer rapidement de nombreuses requêtes authentifiées et disposer d'une large autorité d'action pendant une session. L'API doit traiter chaque appel comme une entrée non fiable, même lorsqu'un humain a approuvé le processus d'agent qui l'a émis.

application/json garantit-il une requête JSON sûre ?

Non. application/json indique uniquement le type de média déclaré. Cela ne garantit ni un JSON valide, ni des membres d'objet uniques, ni des types de champs corrects, ni une structure de requête autorisée. Analysez le contenu strictement, refusez les noms en double, puis validez la valeur obtenue selon le schéma du point d'accès.

Une API JSON doit-elle accepter les requêtes application/x-www-form-urlencoded ?

Refusez-la, sauf si le point d'accès accepte explicitement les données de formulaire et possède pour elles un contrat complet et distinct. Ne convertissez pas les champs du formulaire en objet ressemblant à du JSON avant l'autorisation, car les champs répétés et la syntaxe entre crochets peuvent changer de sens selon les bibliothèques.

Quand une API authentifiée doit-elle accepter multipart/form-data ?

Uniquement si le point d'accès doit téléverser un fichier ou si un protocole client existant impose multipart. Traitez multipart comme un chemin de parsing et de validation distinct, limitez les noms et les en-têtes des parties, et ne l'acceptez pas discrètement comme une autre écriture d'une action JSON.

Un point d'accès POST authentifié peut-il exiger un corps vide ?

Oui, lorsque l'opération ne possède aucune représentation de requête. Une requête avec Content-Length: 0 doit relever d'un contrat explicite sans corps et refuser les types de contenu, les encodages de transfert et les octets qui tenteraient de la transformer en une autre opération.

Quel code d'état une API doit-elle renvoyer pour un Content-Type incorrect ?

Un point d'accès strict renvoie une erreur client avant d'évaluer l'autorisation ou d'exécuter la logique métier. Utilisez 415 pour un type de média non pris en charge, 400 pour une syntaxe incorrecte et 422 lorsqu'une syntaxe valide ne respecte pas le schéma du point d'accès, si ces distinctions correspondent aux conventions de votre API.

Comment tester une API pour détecter un désaccord entre parseurs ?

Testez les requêtes brutes à travers le même point d'entrée public, la même passerelle et le même chemin applicatif qu'en production. Pour chaque opération protégée, faites varier le type de média, les champs en double, les paramètres répétés, les en-têtes des parties multipart, la longueur du corps et l'encodage du contenu. Vérifiez que chaque variante invalide échoue avant la couche d'action.

Une passerelle API ou un WAF peut-il résoudre seul la confusion entre parseurs ?

Non. Un WAF ou une passerelle API peut bloquer les entrées manifestement incorrectes, mais c'est aussi un parseur dans la chaîne et il peut introduire une interprétation différente. L'application qui autorise et exécute l'action doit encore analyser et valider une représentation canonique unique.

L'approbation humaine peut-elle remplacer la validation du schéma à chaque requête ?

L'approbation doit porter sur le processus d'agent et la capacité qui lui est accordée, tandis que l'API doit continuer à valider chaque corps de requête comme s'il provenait d'un code hostile. L'approbation humaine ne valide pas un format et ne peut pas corriger une ambiguïté apparue après l'envoi de la requête par l'agent.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov