Terrain 16 — Sécurité
⏱️ TL;DR — 20 mini-jeux
⭐⭐⭐⭐sur les réflexes de sécurité WordPress : assainir l’entrée, échapper la sortie, nonces, capabilities,$wpdb->prepare, durcissement. Ces règles ne sont pas optionnelles : ce sont elles qui séparent un plugin pro d’une passoire.
⚠️ La devise à graver : « Ne jamais faire confiance à l’entrée. Toujours échapper la sortie. Toujours vérifier les droits. »
🕹️ Mini-jeu 1 — Assainir une entrée ⭐⭐⭐⭐ 💻
But : nettoyer ce qui rentre. Mission : assainis un champ texte reçu. Étapes :
$nom = sanitize_text_field( wp_unslash( $_POST['nom'] ?? '' ) );✅ Solution
Chaque type a son assainisseur : sanitize_text_field, sanitize_email, esc_url_raw, absint, sanitize_textarea_field. wp_unslash retire les antislashs ajoutés par WordPress. Aucune donnée n’entre en base sans assainissement.
🕹️ Mini-jeu 2 — Échapper une sortie HTML ⭐⭐⭐⭐ 💻
But : empêcher le XSS. Mission : affiche une donnée utilisateur en sécurité. Étapes :
echo esc_html( $nom );✅ Solution
esc_html neutralise le HTML/JS injecté (XSS). Échapper se fait au moment de la sortie, au plus près du echo. Assainir ≠ échapper : on fait les deux (entrée et sortie).
🕹️ Mini-jeu 3 — Échapper selon le contexte ⭐⭐⭐⭐ 💻
But : le bon esc_*.
Mission : échappe une URL, un attribut, du HTML.
Étapes :
echo '<a href="' . esc_url( $url ) . '" title="' . esc_attr( $t ) . '">' . esc_html( $label ) . '</a>';✅ Solution
Le contexte dicte la fonction : esc_url (href/src), esc_attr (attributs), esc_html (texte), esc_js (inline JS), esc_textarea. Utiliser le mauvais laisse une faille. Réflexe : « quel contexte ? → quel esc ? ».
🕹️ Mini-jeu 4 — Autoriser un peu de HTML ⭐⭐⭐⭐ 💻
But : garder du gras, filtrer le reste. Mission : affiche un texte riche mais assaini. Étapes :
echo wp_kses_post( $contenu );✅ Solution
wp_kses_post autorise le HTML des articles (gras, liens, listes) et supprime le reste (scripts). wp_kses( $x, $allowed ) permet une liste blanche sur mesure. À utiliser quand esc_html serait trop strict (on veut du HTML).
🕹️ Mini-jeu 5 — Un nonce sur un formulaire ⭐⭐⭐⭐ 💻
But : anti-CSRF. Mission : protège un formulaire admin par nonce. Étapes :
wp_nonce_field( 'wpr_action', 'wpr_nonce' ); // rendu
// au traitement :
check_admin_referer( 'wpr_action', 'wpr_nonce' );✅ Solution
Le nonce prouve que la requête vient bien de ton formulaire (pas d’un site tiers = CSRF). wp_nonce_field le pose, check_admin_referer/wp_verify_nonce le vérifie. Toute action qui modifie un état doit être « noncée ».
🕹️ Mini-jeu 6 — Vérifier une capability ⭐⭐⭐⭐ 💻
But : l’autorisation. Mission : protège une action réservée aux éditeurs de l’article. Étapes :
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( 'Non autorisé' );
}✅ Solution
Le nonce prouve l’origine, pas le droit : il faut aussi current_user_can(). Vérifie la capability (pas le rôle en dur). Nonce + capability = le duo obligatoire de toute action sensible.
🕹️ Mini-jeu 7 — Requête SQL préparée ⭐⭐⭐⭐⭐ 💻
But : anti-injection SQL. Mission : interroge la base avec un paramètre utilisateur. Étapes :
global $wpdb;
$row = $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE ID = %d", $id
) );✅ Solution
$wpdb->prepare avec des placeholders (%d, %s, %f) échappe les valeurs → pas d’injection SQL. Jamais de concaténation de variable dans une requête. Et privilégie l’API haut niveau (WP_Query, get_posts) au SQL brut.
🕹️ Mini-jeu 8 — Ne jamais lire $_GET brut ⭐⭐⭐⭐ 💻
But : valider les paramètres. Mission : lis un ID depuis l’URL en sécurité. Étapes :
$id = isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0;✅ Solution
$_GET/$_POST/$_REQUEST sont hostiles par défaut. On valide/caste systématiquement (absint, sanitize_*). Un ID est un entier positif : absint garantit ça. Ne jamais utiliser une superglobale telle quelle.
🕹️ Mini-jeu 9 — Sécuriser un endpoint REST ⭐⭐⭐⭐⭐ 💻
But : l’autorisation d’API. Mission : protège une route REST d’écriture. Étapes :
register_rest_route( 'wpr/v1', '/book', array(
'methods' => 'POST',
'callback' => 'wpr_create',
'permission_callback' => fn() => current_user_can( 'edit_posts' ),
) );✅ Solution
permission_callback est obligatoire : jamais __return_true sur une écriture. L’API REST contourne l’UI mais pas les permissions — reproduis les vérifs de capability. Faille n°1 des endpoints custom (terrain 17).
🕹️ Mini-jeu 10 — Valider ≠ assainir ⭐⭐⭐⭐ 💻
But : la nuance. Mission : valide qu’un prix est bien numérique avant d’assainir.
✅ Solution
Valider = refuser si non conforme (is_numeric, is_email) ; assainir = forcer une forme propre. Idéalement : valider (rejeter le mauvais) puis assainir (nettoyer le reste). Les deux sont complémentaires, pas interchangeables.
🕹️ Mini-jeu 11 — Cacher la version de WordPress ⭐⭐⭐ 💻
But : réduire la surface d’info.
Mission : retire le générateur du <head>.
Étapes :
remove_action( 'wp_head', 'wp_generator' );✅ Solution
Exposer la version facilite le ciblage de failles connues. On retire wp_generator (terrain 12, jeu 7). Mesure mineure mais standard de durcissement (avec la MAJ régulière, la vraie protection).
🕹️ Mini-jeu 12 — Désactiver l’éditeur de fichiers ⭐⭐⭐ 💻
But : empêcher l’édition de code via l’admin. Mission : ajoute la constante de durcissement. Étapes :
define( 'DISALLOW_FILE_EDIT', true );✅ Solution
Coupe Apparence → Éditeur de fichiers (terrain 11, jeu 19) : un admin compromis ne peut plus injecter de code via l’UI. Standard de prod. Complément : DISALLOW_FILE_MODS interdit aussi l’installation de plugins/thèmes.
🕹️ Mini-jeu 13 — Protéger wp-config.php ⭐⭐⭐⭐ 💻
But : le fichier le plus sensible. Mission : vérifie ses permissions et son emplacement.
✅ Solution
wp-config.php contient identifiants BDD et clés : permissions restrictives (ex. 640), jamais lisible publiquement, idéalement hors du dossier web ou protégé par le serveur. Régénérer les salts invalide les sessions volées.
🕹️ Mini-jeu 14 — Le SVG, arme à double tranchant ⭐⭐⭐⭐ 💻
But : un upload risqué. Mission : explique pourquoi autoriser le SVG (terrain 12, jeu 22) est dangereux.
✅ Solution
Un SVG est du XML qui peut contenir du script (XSS au chargement). Ne l’autorise que pour des rôles de confiance et assainis-le (bibliothèque type SVG Sanitizer, ou plugin dédié). Ne jamais l’ouvrir aux inscrits publics.
🕹️ Mini-jeu 15 — Démonstration d’XSS ⭐⭐⭐⭐ 💻
But : voir la faille pour la comprendre.
Mission : affiche $_GET['q'] sans échapper (en local), constate, puis corrige.
✅ Solution
echo $_GET['q'] avec ?q=<script>alert(1)</script> exécute le script : XSS. La correction : echo esc_html( $_GET['q'] ). Fais-le en local pour ancrer pourquoi on échappe. La théorie devient réflexe quand on a vu la faille tirer.
🕹️ Mini-jeu 16 — Démonstration de CSRF ⭐⭐⭐⭐ 💻
But : comprendre le nonce par l’absence. Mission : un formulaire d’action sans nonce — explique l’attaque possible.
✅ Solution
Sans nonce, un site tiers peut soumettre une requête à ton action au nom d’un admin connecté (CSRF) : suppression, changement de réglage… Le nonce (jeu 5) casse l’attaque car l’attaquant ne peut pas le deviner. D’où : jamais d’action d’état sans nonce.
🕹️ Mini-jeu 17 — Limiter le brute force ⭐⭐⭐ 🛠️
But : protéger le login. Mission : installe « Limit Login Attempts » (terrain 09) et vérifie le blocage.
✅ Solution
Bloquer les tentatives répétées, un 2FA (terrain 10), un login renommé et des mots de passe forts cassent le brute force sur /wp-login.php. Combine avec un pare-feu applicatif (jeu 18) pour une défense en couches.
🕹️ Mini-jeu 18 — Un pare-feu applicatif ⭐⭐⭐⭐ 🛠️
But : filtrer le trafic malveillant. Mission : installe un plugin de sécurité (Wordfence / Solid Security) et active le pare-feu.
✅ Solution
Un WAF (Web Application Firewall) bloque des patterns d’attaque connus, scanne les fichiers, surveille les connexions. Utile en prod, mais ne remplace pas un code propre : la sécurité se joue d’abord dans ton code (jeux 1–10).
🕹️ Mini-jeu 19 — Mises à jour = sécurité ⭐⭐ 🛠️
But : la protection n°1. Mission : vérifie que cœur, thèmes et extensions sont à jour.
✅ Solution
La majorité des piratages exploite un composant non à jour. Mettre à jour (après sauvegarde), retirer l’inutilisé (terrain 08), activer les MAJ auto de sécurité : c’est le geste le plus rentable. Le code parfait sur une version trouée ne sert à rien.
🕹️ Mini-jeu 20 — La check-list de sécurité ⭐⭐⭐⭐ 💻
But : capstone du terrain.
Mission : audite wpr-books-lite (terrain 15) : chaque entrée assainie, chaque sortie échappée, chaque écriture noncée + capability, SQL préparé, REST protégé, uninstall propre.
✅ Solution
Si tu coches tout, ton plugin est sûr par conception. Ces réflexes sont ce que Plugin Check et les reviewers wp.org traquent (Partie 11). La sécurité n’est pas une étape finale : c’est une façon d’écrire chaque ligne. Cap sur le headless (terrain 17).
Terrain suivant : 17 — REST API & headless.