HTTP method override trompe la revue par méthode
HTTP method override peut changer un POST approuvé en DELETE ou PATCH. Testez les verbes tunnelisés et jugez l'action effective.

Un contrôle fondé sur la méthode n'est pas sûr si l'examinateur ne voit que la ligne de requête. Une requête peut arriver en POST, contenir X-HTTP-Method-Override: DELETE, puis atteindre un gestionnaire de suppression après réécriture par un middleware. Si l'écran d'approbation, la passerelle, l'autorisation ou l'audit la classe comme un POST ordinaire, il a contrôlé l'enveloppe et manqué l'action.
Ce comportement n'est pas une règle du protocole HTTP comprise par tous les serveurs. C'est une convention applicative, donc facile à oublier. Sa prise en charge varie selon le framework, l'ordre des middlewares, la route et le déploiement. Il ne faut pas supposer que tout POST cache une destruction. Il faut découvrir où les substitutions sont acceptées, les résoudre avant toute décision de sécurité et rejeter les ambiguïtés.
La méthode transportée et la méthode effective sont deux faits
Une pile qui accepte les substitutions doit suivre deux méthodes : celle de la ligne HTTP et celle que l'application emploie finalement pour le routage. Je les appelle méthode de transport et méthode effective. Elles coïncident souvent. Le cas dangereux apparaît lorsqu'un intermédiaire approuve ou filtre la première tandis qu'un composant ultérieur route selon la seconde.
RFC 9110 indique que le jeton de méthode est la principale source de sens d'une requête. POST demande un traitement propre à la ressource, tandis que DELETE demande au serveur d'origine de retirer l'association entre la ressource cible et sa fonction actuelle. Le RFC ne normalise pas X-HTTP-Method-Override. Cet en-tête appartient à des conventions de compatibilité créées pour des clients ou intermédiaires limités à GET et POST.
Des logiciels courants appliquent encore cette convention. Le middleware method-override d'Express lit par défaut X-HTTP-Method-Override sur un POST. Il modifie req.method et garde l'ancienne valeur dans req.originalMethod. ASP.NET Core propose aussi un middleware dont l'en-tête par défaut est identique. HiddenHttpMethodFilter de Spring suit une autre voie : il lit le paramètre de formulaire _method sur POST et autorise PUT, DELETE et PATCH.
Ces détails précisent une distinction souvent brouillée. « La passerelle a permis POST » décrit le transport. « L'application a exécuté DELETE » décrit le comportement. Une approbation ou une décision d'accès a besoin du second fait. Si le composant décisionnaire ne peut pas déterminer la méthode effective, il doit rejeter les signaux de substitution au lieu de classer silencieusement selon le verbe extérieur.
La présence d'une fonction dans le framework ne prouve pas une exposition. Express doit installer et ordonner le middleware, ASP.NET Core doit l'ajouter, et Spring doit activer et placer le filtre. La configuration réellement déployée, avec les proxys inverses et les middlewares propres à certaines routes, fixe le résultat. Le code donne des pistes, un test qui vérifie l'état apporte la preuve.
Un POST approuvé peut atteindre un gestionnaire DELETE
Une écriture tunnelisée passe lorsque les composants ne s'accordent pas sur la représentation qui fait autorité. Imaginons un agent proposant cette requête à une API de projets :
POST /v1/projects/42 HTTP/1.1
Host: api.example.test
Authorization: Bearer [injected outside the agent]
X-HTTP-Method-Override: DELETE
Content-Length: 0
La couche d'approbation affiche « POST /v1/projects/42 » et applique une règle qui permet POST pour cette session. Un proxy inverse transmet l'en-tête inconnu. RFC 9110 demande généralement aux proxys de transmettre les champs inconnus, sauf blocage ou transformation explicite. Dans l'application, le middleware change la méthode avant le routage. Le routeur choisit DELETE et le projet disparaît.
Il ne faut pas une autorisation DELETE défectueuse pour produire cet échec. L'application peut autoriser correctement cette identité à supprimer. Le défaut se trouve plus tôt : une personne ou un automate a approuvé une action matériellement différente parce que son classificateur a ignoré une entrée admise. La même divergence peut toucher un pare-feu applicatif, une limite de débit, un contrôle CSRF, les mesures et les journaux d'accès.
L'ordre des middlewares détermine quel contrôle voit quel fait. La documentation Express est directe : la substitution doit passer avant tout middleware qui doit connaître la méthode. Le conseil est juste pour le routage et le CSRF dans un processus. Il ne répare pas une passerelle externe qui a déjà décidé à partir de la ligne de requête.
Un second échec est plus discret. Le journal périphérique écrit POST, celui de l'application écrit DELETE, et l'enquêteur les traite comme deux requêtes. Un identifiant commun peut les relier, à condition que les deux côtés le conservent et que l'équipe compare les champs. Un seul enregistrement normalisé de l'action réduit les erreurs.
Ne déduisez pas l'impact du mot DELETE seul. RFC 9110 précise que DELETE retire l'association entre une ressource et sa fonction actuelle ; la destruction et la récupération du stockage dépendent de l'application. Une route DELETE peut archiver, désactiver, mettre en file ou effacer. Examinez sa conséquence réelle, pas une définition générique.
Testez le point d'accès avec une matrice contrôlée
Un test fiable compare l'état produit par les méthodes natives, les en-têtes de substitution et les paramètres sur une ressource jetable. Exécutez-le en préproduction ou sur un dispositif prévu pour les essais destructifs. Utilisez une identité limitée comme le véritable appelant et recréez la ressource avant chaque cas afin qu'une suppression ne contamine pas le suivant.
Commencez par un POST témoin sans substitution. Enregistrez son statut, son corps et l'état de la ressource. Envoyez ensuite un DELETE natif pour confirmer la route et les droits. Rejouez enfin POST avec chaque convention. Une sonde minimale ressemble à ceci :
BASE='https://staging.example.test'
ID='override-probe-17'
curl -sS \n -D response.headers \n -o response.body \n -w 'case=x-http-method-override outer=POST status=%{http_code} bytes=%{size_download}
' \n -X POST "$BASE/v1/projects/$ID" \n -H 'Authorization: Bearer test-token' \n -H 'X-HTTP-Method-Override: DELETE'
curl -sS \n -o state.body \n -w 'verify=read-after-request status=%{http_code} bytes=%{size_download}
' \n -H 'Authorization: Bearer test-token' \n "$BASE/v1/projects/$ID"
La sortie a volontairement une forme stable pour l'intégration continue :
case=x-http-method-override outer=POST status=204 bytes=0
verify=read-after-request status=404 bytes=71
Cet exemple illustre une substitution qui a supprimé la ressource ; ce n'est pas un statut universel. Certaines API renvoient 200 avec un document, 202 pour une suppression différée ou une représentation encore lisible après suppression logique. Définissez le succès d'après le contrat de votre application.
Utilisez une matrice, pas une suite improvisée. Le témoin est POST sans substitution et doit garder le comportement POST normal. Le cas natif est DELETE sans substitution. Envoyez ensuite POST avec X-HTTP-Method-Override: DELETE, X-HTTP-Method: DELETE et X-Method-Override: DELETE. Finissez avec ?_method=DELETE dans la requête et _method=DELETE dans un formulaire. Chaque cas doit suivre votre conception explicite ou échouer sans modifier l'état.
Le guide de test web d'OWASP nomme les trois en-têtes et recommande de rejouer une requête avec eux lorsqu'une méthode restreinte est refusée. Je conseille d'aller au-delà du statut, car la différence peut venir de la validation, du routage ou d'un intermédiaire. Vérifiez la ressource, sa version et tout travail mis en file.
Si vous maîtrisez la pile, capturez chaque étape : journal périphérique, événement d'approbation, journal applicatif, route sélectionnée et résultat sur les données. Vous cherchez un désaccord. Un résultat sûr est un rejet cohérent ou une normalisation volontaire suivie de la même autorisation que DELETE natif.
Le statut est un indice, le changement d'état une preuve
Un statut HTTP ne suffit pas à dire qu'une substitution s'est exécutée. Un 204 suivi d'une ressource absente est probant. Un 200 peut être une réponse POST, un reçu de suppression ou une erreur mal encodée. Un 405 peut provenir du bord avant le middleware alors qu'une autre route ou un autre type de contenu l'atteint encore.
Fondez l'assertion sur une ressource témoin avec identifiant unique et version initiale connue. Avant la sonde, récupérez-la et gardez son marqueur de version ou son empreinte. Après, lisez-la à nouveau et examinez l'opération retournée. Pour une mise à jour, choisissez un champ sans danger et évident. Pour une suppression, connaissez son caractère physique, logique ou différé.
La comparaison des réponses reste utile. Sauvegardez statut, en-têtes choisis, taille et empreinte du corps. Comparez la substitution au POST simple et au DELETE natif. Si réponse et état ressemblent à DELETE, la conclusion est solide. Si la réponse ressemble à POST mais que l'état suit DELETE, la couche de réponse ou de journalisation utilise peut-être la méthode extérieure.
Les redirections exigent leur propre contrôle. Le client peut changer de comportement sur 301, 302, 303, 307 ou 308, et les outils conservent différemment méthode et corps. Désactivez d'abord le suivi et notez Location. Suivez ensuite volontairement chaque saut. Ne mélangez pas redirection et substitution dans un résultat opaque.
Une API asynchrone demande une observation plus longue, pas des suppositions. Si la réponse contient un identifiant d'opération, interrogez-le selon le contrat. Sans référence durable, consultez la file ou le journal en préproduction. Marquez un cas indéterminé comme tel, pas comme sûr.
Vérifiez aussi l'absence d'effets. Une substitution refusée ne doit ni créer, modifier, archiver, mettre en file, envoyer un courriel, ni déclencher un webhook privilégié. Un 4xx propre peut tromper si un composant a déjà validé un effet. Instrumentez la ressource pour détecter cet ordre.
Les variantes et ambiguïtés appartiennent au même test
Tester seulement l'orthographe canonique ignore le code de compatibilité et les divergences d'analyse. Les noms courants sont X-HTTP-Method-Override, X-HTTP-Method et X-Method-Override, mais l'application peut définir un lecteur personnalisé. Requêtes et formulaires peuvent porter _method, et les anciennes intégrations emploient parfois des noms propres à un fournisseur.
La casse du nom n'est pas une convention distincte. RFC 9110 déclare les noms de champ insensibles à la casse, donc x-http-method-override et X-HTTP-Method-Override désignent le même champ. Un filtre limité à une graphie est défectueux même si la bibliothèque normalise les noms.
Les valeurs dupliquées ou contradictoires révèlent un problème plus difficile. RFC 9110 permet de combiner certaines lignes répétées avec des virgules et demande de considérer les doublons même pour un champ supposé unique. Les en-têtes de substitution n'ont pas de règle normalisée commune. Express prend la première occurrence, tandis que plusieurs lecteurs peuvent créer une priorité entre des noms différents.
Ajoutez x-http-method-override: DELETE et exigez le même résultat. Envoyez deux DELETE identiques et exigez un résultat documenté ou un rejet. Envoyez PUT puis DELETE, puis deux noms avec des valeurs distinctes ; rejetez ces conflits. Rejetez aussi PUT, DELETE, PATCH natif avec substitution DELETE sans contrat explicite, et DELETE accompagné de _method=PATCH.
Ne vous arrêtez pas à DELETE. Testez PUT et PATCH, car le contrôle peut distinguer création, remplacement, mise à jour et suppression. Un jeton non pris en charge sert de témoin négatif. Évitez TRACE et CONNECT hors d'un périmètre explicite ; ils sollicitent d'autres fonctions sans améliorer cette preuve.
Les espaces et la casse de la valeur peuvent révéler une normalisation incohérente. Envoyez delete, DELETE si le client le permet, et une valeur vide. Ne normalisez que les formes documentées, validez contre un ensemble explicite et rejetez le reste. Choisir la première valeur lisible recréera le contournement lors d'un changement de proxy.
Les requêtes d'agents facilitent l'angle mort
Les agents d'IA ne créent pas cette faiblesse, mais aggravent une revue incomplète. Ils peuvent composer des en-têtes arbitraires, réutiliser des exemples et retenter un DELETE refusé sous forme de POST tunnelisé sans comprendre la frontière d'approbation. Face à une carte compacte, une personne regarde surtout le verbe et le chemin visibles.
Traitez chaque partie générée comme une entrée d'action non fiable, y compris les en-têtes qui semblent être de simples métadonnées. Injecter les identifiants hors du modèle réduit leur exposition, mais ne rend pas l'opération bénigne. Chemin, méthode, en-têtes, requête et corps choisis arrivent dans un système qui peut les interpréter ensemble.
Le schéma de l'outil peut réduire l'ambiguïté avant la création. Un outil HTTP générique avec en-têtes libres permet les substitutions. Un outil de suppression propre à une route peut nommer l'action et exclure ces champs. Il se révise plus facilement, à condition de ne pas conserver une échappatoire cachée.
Si l'outil générique est indispensable, analysez la proposition avant de l'afficher. L'analyseur doit partager l'inventaire et les priorités de l'exécuteur. Figez l'action après approbation. Si un intermédiaire transforme ensuite les en-têtes, incluez sa transformation déterministe dans l'action examinée ou demandez une nouvelle approbation.
Les nouvelles tentatives comptent aussi. POST n'est ni anodin ni idempotent par nature. RFC 9110 le définit largement et de nombreux POST modifient des données sans substitution. L'outil ne peut pas déduire qu'un nouvel essai est sûr d'après le verbe extérieur, ni traiter chaque POST comme destructif. Il lui faut la route et la méthode effective.
Séparez les identités de test et de production. Un droit trop large fera réussir toutes les sondes et masquera les écarts. Utilisez le rôle limité réel, confirmez la même décision pour les deux formes et consignez ce que l'humain a vu. Le défaut est prouvé lorsque proposition, affichage, exécution et état forment une seule chaîne.
Le premier contrôle doit classer l'action effective
Toute décision fondée sur la méthode doit suivre un résolveur unique ou refuser toutes les substitutions. Cela vaut pour approbation, autorisation, CSRF, routage, limites et audit. Répéter une analyse improvisée dans chaque contrôle garantit les divergences.
Représentez explicitement la résolution :
{
"transport_method": "POST",
"effective_method": "DELETE",
"override_source": "header:x-http-method-override",
"override_value": "DELETE",
"target": "/v1/projects/42",
"ambiguous": false
}
Le résolveur doit inventorier chaque en-tête et paramètre, recueillir toutes les valeurs et rejeter plusieurs candidats distincts. Il ne doit accepter la substitution que depuis des méthodes extérieures permises, généralement POST. Il valide une liste volontaire et produit un enregistrement immuable pour les contrôles suivants.
Placez l'autorisation après la résolution et liez-la à la route et à la méthode effective. « Cette identité peut-elle faire POST ici ? » est trop faible si POST est un tunnel. Demandez si elle peut faire DELETE sur cette ressource. Appliquez la même autorisation à la forme native et à la forme tunnelisée.
L'écran d'approbation doit montrer d'abord la conséquence et les deux représentations si elles diffèrent. DELETE /v1/projects/42 via POST override montre l'action et le transport. Cacher l'en-tête dans un panneau brut oblige l'examinateur à trouver le danger sous pression.
Si une passerelle ne peut reproduire les règles de l'application, ne lui apprenez pas un sous-ensemble. Rejetez tout signal connu avant approbation ou placez l'approbation après une normalisation fiable. Une passerelle qui comprend un en-tête face à une application qui en comprend trois donne une fausse couverture.
Séparez résolution et sens métier. Le résolveur conclut DELETE ; la route décide s'il s'agit d'archiver, révoquer, détacher ou effacer. Une approbation à fort impact exige une description liée à la route. La méthode normalisée est nécessaire, mais un verbe ne décrit pas tout.
Retirer un seul en-tête ne suffit pas
Supprimer X-HTTP-Method-Override au proxy n'est sûr que si aucun client ne doit employer de tunnel. La mesure est populaire car elle est simple et bloque la première démonstration. Elle échoue si un autre en-tête, _method, une route directe ou un middleware ultérieur reste actif.
Le guide YARP de Microsoft nomme les trois en-têtes, précise qu'ils sont transmis par défaut et conseille une transformation de suppression pour empêcher le contournement. C'est un durcissement utile, pas la preuve que la destination ignore les paramètres ni une protection du trafic qui contourne YARP.
Choisissez l'un de deux modèles. Sans utilisateur légitime, désactivez le middleware et rejetez tous les signaux au bord. Si des clients en dépendent, documentez une seule entrée, rejetez les autres, normalisez avant les contrôles et testez les deux formes comme la même action.
Inventoriez d'abord code et configuration. Cherchez middleware, wrappers, _method, les trois noms et toute affectation de méthode. Vérifiez les groupes de routes et les vieux modules de compatibilité conservés lors de mises à niveau.
Examinez ensuite le chemin réseau. Distribution, équilibrage, maillage, proxy, serveur et framework peuvent normaliser et journaliser différemment. Vérifiez qu'aucune route publique ne contourne le rejet. Un réseau interne ne rend pas fiables des en-têtes choisis par l'appelant.
Le retrait peut casser des clients, ce qui explique les reports. Mesurez l'usage par la présence, sans secrets ni corps complets. Annoncez l'abandon, faites échouer clairement les tests et retirez serveur et règle ensemble. Conserver une compatibilité cachée coûte plus que migrer ses derniers utilisateurs.
Les journaux doivent garder les deux méthodes
Un audit doit conserver méthode de transport, méthode effective, source, valeur normalisée, route, identité, cible, décision, réponse et résultat. Sans les deux méthodes, impossible d'expliquer le passage de POST à DELETE. Sans résultat, on ne voit que l'intention.
Consignez aussi les rejets ambigus. Un conflit reste important même sans gestionnaire. Stockez noms et jetons normalisés, pas les autorisations ni des corps sans rapport. Limitez et échappez les valeurs invalides afin que des caractères de contrôle ne falsifient pas le journal.
Reliez les couches avec un identifiant créé au premier saut fiable. Remplacez ou espacez tout identifiant externe. Bord, résolveur, approbation, application et tâche asynchrone doivent porter le même identifiant.
Nommez les champs précisément. method invite chaque composant à y mettre un sens différent. Employez transport_method pour la ligne et effective_method pour l'action. Mappez originalMethod volontairement, sans supposer le même sens partout.
Sallyport exécute l'appel HTTP de l'agent sans lui exposer l'identifiant secret et enregistre l'appel dans Activity. Cette séparation ne sécurise pas une substitution ; la revue doit encore classer la méthode effective avant exécution.
La preuve d'intégrité compte après une exécution autonome. Un journal enchaîné par hachage rend une modification visible, mais ne restaure pas une sémantique absente. Consignez l'action normalisée au moment de décider, pas dans un traitement ultérieur qui tente de l'inférer.
Le tableau de revue doit afficher les écarts. Compter transport_method != effective_method par service et source révèle le trafic inattendu. Alertez sur les nouvelles sources, l'ambiguïté et les méthodes destructives dont l'approbation n'a gardé que l'extérieur.
Un test de régression doit échouer de façon sûre
La correction durable est un test contractuel sur le chemin déployé qui échoue lorsqu'une substitution non approuvée modifie l'état. Un test unitaire du résolveur ne détecte ni règle de proxy disparue, ni middleware déplacé, ni nouvel accès.
Écrivez le résultat attendu de chaque cas. Si le tunnel est désactivé, chaque signal doit produire un 4xx documenté sans modification. Si une convention est admise, son DELETE doit correspondre au natif pour l'autorisation, l'approbation, la route et l'audit. Conflits et jetons inconnus échouent avant tout effet.
Incluez ces assertions :
- l'approbation nomme la méthode effective ;
- les formes native et tunnelisée reçoivent la même autorisation ;
- un rejet ne change ni version ni compteur d'effets ;
- les deux méthodes partagent un identifiant fiable ;
- l'accès direct ne contourne pas la normalisation.
Exécutez la suite lors d'un changement de proxy, framework, authentification, approbation, route ou outil d'agent. Un passage nocturne détecte la dérive et un verrou de publication les changements voulus. Isolez la ressource des données humaines.
Traitez toute nouvelle convention comme un changement de sécurité, même si le framework parle de compatibilité. Exigez un responsable, un besoin, une priorité, une méthode extérieure, une liste effective et un critère de retrait. Sinon une ligne de middleware étend silencieusement le vocabulaire des contrôles.
Un système propre porte une description d'action de l'entrée au résultat. Si le réseau dit POST et l'application DELETE, la différence doit être visible avant qu'une personne ou un composant dise oui. Plus tard, il ne reste que l'enquête.
FAQ
Qu'est-ce que HTTP method override ?
C'est une convention applicative qui transporte un verbe effectif comme DELETE dans une requête souvent envoyée en POST. Un middleware lit un en-tête ou paramètre et change le routage.
X-HTTP-Method-Override est-il un en-tête HTTP standard ?
Non. RFC 9110 définit les méthodes mais ne normalise pas X-HTTP-Method-Override. Le support dépend du framework, middleware, API ou code, donc testez le service déployé.
Quels en-têtes de substitution faut-il tester ?
Testez X-HTTP-Method-Override, X-HTTP-Method et X-Method-Override. Vérifiez aussi _method dans requêtes et formulaires, ainsi que les lecteurs personnalisés.
Un POST peut-il vraiment supprimer une ressource ?
Oui, si la destination accepte X-HTTP-Method-Override: DELETE et route comme DELETE. Prouvez-le avec une ressource jetable et son état ; le statut seul ne suffit pas.
Le proxy doit-il bloquer tous ces en-têtes ?
Oui si aucun client légitime n'utilise de tunnel. Sinon normalisez une convention documentée avant approbation et autorisation, et rejetez les autres.
La casse de l'en-tête change-t-elle le résultat ?
Elle ne devrait pas, car les noms de champ HTTP sont insensibles à la casse. Un filtre limité à une graphie peut diverger du framework.
Comment traiter les en-têtes dupliqués ?
Rejetez les valeurs répétées ou contradictoires comme ambiguës. Proxys et bibliothèques peuvent les combiner ou les choisir différemment.
Un statut 405 prouve-t-il que la substitution est désactivée ?
Non. Une couche peut renvoyer 405 tandis qu'une autre route, un autre en-tête ou paramètre fonctionne. Vérifiez état et journaux sur le chemin réel.
Que doit afficher l'écran d'approbation ?
Montrez d'abord l'action effective, par exemple DELETE /v1/projects/42 via POST override. Gardez la méthode extérieure et la source comme détails.
Que doit contenir le journal d'audit ?
Conservez les deux méthodes, la source, la route, l'identité, la cible, la décision, la réponse et le résultat sous un identifiant fiable. Ne copiez pas les secrets.