vendredi 25 septembre 2026

À propos

Contact

Sécurité

Comprendre wp_verify_nonce et sa fenêtre de validité réelle

Les nonces WordPress ne sont pas de vrais jetons de sécurité et expirent selon une logique en deux temps souvent mal comprise. Explication du mécanisme et de ses limites.

Par Clément Hadrot • 7 août 2020 • 6 min de lecture • Aucun commentaire
Comprendre wp_verify_nonce et sa fenêtre de validité réelle

« Pourquoi mon lien d’action fonctionne encore le lendemain matin, alors que la documentation dit que les nonces expirent après douze heures ? » Cette question, posée par un développeur sur un projet client, résume assez bien la confusion la plus fréquente autour des nonces WordPress. La réponse tient en un mot : ticks. Et comprendre ce mécanisme évite deux erreurs opposées, tout aussi fréquentes : croire qu’un nonce protège contre tout, ou au contraire le traiter comme un simple cosmétique sans le vérifier correctement côté serveur.

Un nonce, dans le vocabulaire WordPress, n’a rien à voir avec un jeton de session ou une clé secrète. C’est une chaîne courte, générée à partir d’un secret côté serveur (le sel NONCE_SALT combiné à l’identifiant de l’utilisateur et à l’action concernée), qui permet de vérifier qu’une requête provient bien d’une page générée légitimement par WordPress, dans une fenêtre de temps limitée. Il protège contre le CSRF, pas contre le vol de session ni contre un utilisateur malveillant déjà authentifié qui abuserait de ses propres droits.

Le mécanisme des « ticks » de douze heures

Quand WordPress génère un nonce via wp_create_nonce( 'mon_action' ), il ne calcule pas une expiration absolue à partir de l’heure courante. Il calcule un numéro de « tick », c’est-à-dire une tranche de temps de douze heures depuis l’epoch, déterminée par la constante DAY_IN_SECONDS divisée en deux. La fonction interne wp_nonce_tick() renvoie ce numéro, et le nonce est généré en mélangeant ce tick avec l’action, l’utilisateur et le sel.

Lors de la vérification, wp_verify_nonce( $nonce, 'mon_action' ) recalcule deux valeurs possibles : celle du tick actuel, et celle du tick précédent. Si le nonce fourni correspond à l’une des deux, il est accepté. C’est ce qui explique la confusion initiale : un nonce généré à 23h50 reste valide non pas jusqu’à minuit, mais jusqu’à la fin de la tranche de douze heures suivante, soit potentiellement près de 24 heures plus tard selon le moment exact de la génération.

function wp_verify_nonce( $nonce, $action = -1 ) {
    $nonce = (string) $nonce;
    $user  = wp_get_current_user();
    $uid   = (int) $user->ID;
    // ... récupération d'un identifiant de session pour les utilisateurs déconnectés

    $i = wp_nonce_tick( $action );

    // Nonce généré durant la tranche actuelle : match "1"
    $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    if ( hash_equals( $expected, $nonce ) ) {
        return 1;
    }

    // Nonce généré durant la tranche précédente : match "2"
    $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    if ( hash_equals( $expected, $nonce ) ) {
        return 2;
    }

    return false;
}

Ce code est une reconstitution simplifiée du mécanisme réel de WordPress, mais elle illustre le point essentiel : la valeur de retour n’est pas un simple booléen. 1 signifie un nonce généré durant la tranche courante, 2 un nonce généré durant la tranche précédente (donc plus ancien, potentiellement proche de son expiration), et false un nonce invalide ou expiré. La plupart des développeurs testent uniquement if ( wp_verify_nonce( ... ) ), ce qui fonctionne parce que 1 et 2 sont tous deux « truthy » en PHP, mais qui masque une information parfois utile : distinguer un nonce encore frais d’un nonce sur le point d’expirer, pour par exemple régénérer proactivement un jeton côté interface avant qu’il ne devienne invalide.

Ce que le nonce protège vraiment, et ce qu’il ne protège pas

L'essentiel à retenir : Un nonce n'est pas secret, il est vérifiable ; La validité repose sur des « ticks » de 12 heures ; wp_verify_nonce peut renvoyer 1 ou 2, pas juste vrai ou faux

Le nonce répond à une question précise : « cette requête a-t-elle été initiée depuis une page que WordPress a lui-même générée pour cet utilisateur, récemment ? ». Il ne répond à aucune autre question de sécurité :

  • Il ne remplace pas current_user_can(). Un nonce valide prouve l’origine de la requête, pas que l’utilisateur a le droit d’effectuer l’action demandée. Les deux vérifications sont complémentaires et doivent toujours être présentes ensemble.
  • Il n’est pas confidentiel au sens cryptographique. Il apparaît dans le code source HTML de la page, dans l’URL parfois, et peut être lu par n’importe quel script qui a accès à cette page — y compris un script XSS injecté sur le même domaine. Un nonce ne protège donc pas contre le XSS, seulement contre une requête forgée depuis un domaine tiers.
  • Il n’empêche pas le rejeu pendant sa fenêtre de validité. Un attaquant qui intercepte un nonce valide (par exemple via un XSS) peut l’utiliser plusieurs fois tant que la tranche horaire n’a pas changé, à condition que l’action visée le permette elle-même.

Adapter la vérification à la criticité de l’action

Pour une action à faible risque (masquer une notice d’administration, par exemple), la vérification standard avec check_admin_referer() ou check_ajax_referer() suffit largement. Pour une action sensible (suppression définitive de données, modification d’un rôle), on peut combiner le nonce avec une vérification de fraîcheur plus stricte, en exploitant justement la valeur de retour de wp_verify_nonce() :

$verif = wp_verify_nonce( $_POST['_wpnonce'], 'suppression_definitive' );

if ( ! $verif ) {
    wp_die( esc_html__( 'Jeton de sécurité invalide ou expiré, veuillez recharger la page.', 'moncpt' ) );
}

if ( 2 === $verif ) {
    // Nonce généré il y a potentiellement plusieurs heures : on redemande confirmation
    // avant une action destructrice, plutôt que d'accepter silencieusement.
}

Sur nos formations internes, nous résumons la règle ainsi : le nonce garantit la fraîcheur et l’origine d’une requête, jamais l’autorisation de celui qui l’envoie. Les deux vérifications, nonce et capacité, doivent toujours voyager ensemble.

Ce qui n’est pas couvert ici

Ce fonctionnement interne des nonces ne traite pas des vulnérabilités CSRF spécifiques aux extensions tierces qui implémentent leur propre système de jetons en dehors de l’API WordPress, un sujet abordé séparément. La bonne compréhension de la fenêtre de validité en deux temps reste néanmoins la base indispensable pour juger si une implémentation personnalisée respecte ou non l’esprit du mécanisme natif.

Partager :

À propos de l'auteur

Clément Hadrot

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

Voir tous ses articles

Dans la même veine

À lire aussi