Le WordPress d'aujourd'hui, décodé pour les développeurs

E-commerce

Antipatterns : surcharger woocommerce_checkout_process sans vérifier le nonce

Un audit de code révèle une validation de commande contournable : le hook checkout est bien accroché, mais rien ne vérifie que la requête vient réellement du formulaire.

Par WordPress Développement • 3 novembre 2022 • 4 min de lecture • Aucun commentaire
Antipatterns : surcharger woocommerce_checkout_process sans vérifier le nonce

Comparé à une simple validation JavaScript côté client, un hook PHP accroché sur woocommerce_checkout_process paraît robuste : il s’exécute côté serveur, il peut bloquer la commande, il semble donc à l’abri des manipulations. C’est exactement ce raisonnement qui a conduit un audit de code, mené sur une boutique vendant des produits sur devis avec un champ « numéro de bon de commande » obligatoire, à conclure que la validation était solide. Elle ne l’était pas.

Le code fonctionnait parfaitement dans le navigateur, avec le formulaire de paiement standard. Le problème est apparu en testant un scénario que personne n’avait envisagé : une requête POST construite directement vers /?wc-ajax=checkout, en dehors de toute page de paiement, sans jamais charger le formulaire ni son jeton de sécurité.

Ce qu’on voit

Le code incriminé ressemblait à ceci, extrait presque à l’identique du dépôt audité :

add_action( 'woocommerce_checkout_process', function () {
    if ( empty( $_POST['numero_bon_commande'] ) ) {
        wc_add_notice( 'Le numéro de bon de commande est obligatoire.', 'error' );
    }
} );

À première vue, rien ne cloche : le champ est bien contrôlé, un message d’erreur bloque la validation si le champ est vide. Le hook woocommerce_checkout_process est d’ailleurs le hook officiel documenté pour ce type de vérification, ce qui renforçait la confiance de l’équipe dans sa solidité.

Pourquoi c’est un problème

Le hook woocommerce_checkout_process se déclenche dès que WooCommerce traite une soumission de paiement, quelle que soit son origine — y compris une requête forgée qui ne provient jamais du formulaire HTML réel. Rien dans ce code ne vérifie qu’un jeton de sécurité valide (un nonce WordPress) accompagne la requête. Or WooCommerce génère bien un nonce sur la page de paiement (woocommerce-process-checkout-nonce), mais ne le vérifie pas automatiquement pour les champs personnalisés ajoutés par un développeur.

L'essentiel à retenir : Un hook métier n'est pas une preuve d'origine de la requête ; wp_verify_nonce() doit accompagner toute action sensible au checkout ; Un champ personnalisé obligatoire peut être contourné sans ce contrôle

Concrètement, un script externe peut poster directement un champ numero_bon_commande rempli avec n’importe quelle valeur (y compris une valeur copiée d’une autre commande légitime), sans jamais passer par les contrôles métier plus larges qu’un formulaire réel aurait pu imposer côté JavaScript. Le contournement ne demande ni compte utilisateur ni accès privilégié : une simple requête HTTP suffit.

La confusion entre « hook métier » et « origine vérifiée »

L’erreur de fond est de confondre deux notions distinctes : le fait qu’un hook s’exécute côté serveur (ce qui est vrai et rassurant) et le fait que la requête qui le déclenche provient bien du parcours attendu (ce qui ne l’est pas du tout, sans vérification explicite). WooCommerce fournit les outils pour cette seconde vérification, mais ne l’impose pas automatiquement sur les hooks de validation personnalisés.

Quoi faire

Le correctif consiste à vérifier explicitement le nonce généré par WooCommerce avant de traiter la validation :

add_action( 'woocommerce_checkout_process', function () {
    if ( ! isset( $_POST['woocommerce-process-checkout-nonce'] )
        || ! wp_verify_nonce( $_POST['woocommerce-process-checkout-nonce'], 'woocommerce-process_checkout' ) ) {
        wc_add_notice( 'Session expirée, veuillez recharger la page.', 'error' );
        return;
    }

    if ( empty( $_POST['numero_bon_commande'] ) ) {
        wc_add_notice( 'Le numéro de bon de commande est obligatoire.', 'error' );
    }
} );

Cette vérification ne rend pas la commande impossible à forger par un attaquant déterminé — un nonce peut être récupéré en chargeant la page légitimement avant l’envoi — mais elle élimine les scripts automatisés génériques et impose au minimum un passage réel par le parcours prévu, ce qui suffit dans l’immense majorité des cas rencontrés.

  • Toujours vérifier le nonce woocommerce-process-checkout-nonce dans un hook de validation personnalisé.
  • Ne jamais faire confiance à un champ $_POST sans échappement ni contrôle de format.
  • Documenter, dans le code, pourquoi chaque champ personnalisé est considéré comme fiable ou non.

Bilan de l’audit

Ce n’est pas un cas isolé : sur plusieurs bases de code auditées, la même confusion revient dès qu’un développeur ajoute un champ de paiement personnalisé sans relire la documentation sur la sécurisation des formulaires. Ce constat ne s’étend volontairement pas à la sécurisation des points d’entrée de l’API REST, qui répond à une logique de jetons différente et mérite un traitement à part.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi