Un point d’entrée accessible via wp_ajax_nopriv_ répond à n’importe quel visiteur, connecté ou non, ce qui en fait par nature la surface la plus exposée d’une extension. Contrairement à un endpoint réservé aux membres, il ne peut s’appuyer sur aucune vérification de session pour filtrer les requêtes légitimes des tentatives malveillantes.
Beaucoup de développeurs découvrent ce hook en pensant qu’un simple nonce suffit à sécuriser l’action. C’est une erreur : le nonce protège contre les requêtes forgées depuis un autre site (CSRF), pas contre un visiteur qui envoie volontairement des données malformées ou hostiles directement vers l’URL d’admin-ajax.php.
Déclarer les deux hooks selon le public visé
Une action AJAX destinée aux visiteurs non connectés s’enregistre avec wp_ajax_nopriv_{action}. Si la même logique doit aussi répondre aux utilisateurs connectés, il faut déclarer en plus wp_ajax_{action} : les deux hooks sont indépendants, WordPress ne les fait pas cohabiter automatiquement.
add_action( 'wp_ajax_wpm_newsletter_inscription', 'wpm_traiter_inscription' );
add_action( 'wp_ajax_nopriv_wpm_newsletter_inscription', 'wpm_traiter_inscription' );
function wpm_traiter_inscription() {
check_ajax_referer( 'wpm_newsletter_nonce', 'nonce' );
$email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
if ( empty( $email ) || ! is_email( $email ) ) {
wp_send_json_error( array( 'message' => 'Adresse invalide.' ), 400 );
}
// Traitement métier ici.
wp_send_json_success( array( 'message' => 'Inscription enregistrée.' ) );
}
Le nonce vérifie l’origine, pas le contenu
check_ajax_referer() confirme que la requête provient bien d’une page où le nonce a été correctement généré et affiché, généralement via wp_localize_script() ou wp_add_inline_script(). Cela empêche un autre site d’envoyer discrètement des requêtes au nom d’un visiteur qui a ouvert une page infectée dans un autre onglet.

Mais un nonce valide n’apporte aucune garantie sur la nature des données envoyées. Rien n’empêche un visiteur d’inspecter la requête réseau, de copier le nonce affiché sur la page et de renvoyer manuellement des centaines de requêtes avec des valeurs différentes. C’est pourquoi la validation des données reste une étape distincte et obligatoire, quel que soit le résultat du nonce.
Sanitiser à l’entrée, échapper à la sortie
- Utiliser
sanitize_email(),sanitize_text_field()ouabsint()selon le type de donnée attendu - Ne jamais faire confiance à un champ caché du formulaire pour un identifiant de contenu sensible : le revérifier côté serveur
- Encapsuler systématiquement l’entrée brute avec
wp_unslash()avant sanitisation, WordPress ajoutant des antislashs à$_POST
Limiter ce qu’un point d’entrée public peut réellement faire
Un point d’entrée ouvert aux visiteurs non connectés ne doit jamais permettre une action qui modifie un contenu existant, écrit un fichier arbitraire ou déclenche l’envoi massif d’e-mails sans limite de fréquence. Si la fonctionnalité l’exige malgré tout, une limitation de débit devient indispensable, ne serait-ce qu’un compteur simple stocké en transient par adresse IP.
$cle = 'wpm_limite_' . md5( $_SERVER['REMOTE_ADDR'] );
$compteur = (int) get_transient( $cle );
if ( $compteur >= 5 ) {
wp_send_json_error( array( 'message' => 'Trop de tentatives, réessayez plus tard.' ), 429 );
}
set_transient( $cle, $compteur + 1, MINUTE_IN_SECONDS * 10 );
Répondre proprement, sans fuite d’information
Les fonctions wp_send_json_success() et wp_send_json_error() encapsulent la réponse dans un format JSON cohérent et appellent wp_die() en interne, ce qui évite d’oublier l’arrêt du script après l’envoi. Un message d’erreur générique côté visiteur évite en outre de révéler des détails d’implémentation utiles à un attaquant, comme la structure exacte d’une requête SQL sous-jacente.
| Vérification | Rôle | Fonction WordPress |
|---|---|---|
| Origine de la requête | Anti-CSRF | check_ajax_referer() |
| Format des données | Anti-injection | sanitize_email(), absint() |
| Fréquence des appels | Anti-abus | get_transient(), set_transient() |
| Réponse | Format cohérent | wp_send_json_success(), wp_send_json_error() |
Sur un point d’entrée public, la question à se poser n’est jamais « est-ce que ce visiteur va bien se comporter » mais « que se passe-t-il si quelqu’un envoie exactement le contraire de ce que j’attends ».
Pour aller plus loin
Un point d’entrée AJAX public bien conçu combine toujours trois couches : la vérification d’origine par le nonce, la validation stricte du contenu, et une limite raisonnable sur la fréquence des appels. Aucune des trois ne remplace les deux autres, et c’est précisément leur combinaison qui transforme une action ouverte à tous en une fonctionnalité robuste plutôt qu’en une porte laissée entrouverte.