# Anatomie d’une faille XSS dans une extension WordPress : cas d’école

> Un cas pédagogique générique pour apprendre à repérer une faille XSS en revue de code avant qu'elle ne parte en production, et à la corriger proprement.

- Auteur : Clément Hadrot
- Publié le : 2021-08-25
- Mis à jour le : 2021-08-25
- Catégorie : Sécurité
- URL : https://wpmoderne.dev.wordpress-developpement.fr/securite/anatomie-faille-xss-extension/

## L’essentiel

- Une faille XSS naît presque toujours d'une donnée affichée sans échappement
- Le paramètre d'URL est le vecteur le plus fréquent dans les extensions
- Trois lignes de correction suffisent généralement à fermer la faille

Pour illustrer concrètement comment naît une faille XSS dans une extension WordPress, prenons un cas d'école, entièrement fictif et générique, construit à partir des schémas que je retrouve le plus souvent en audit de code. Aucune extension réelle n'est visée ici : c'est un exemple pédagogique destiné à apprendre à repérer ce type de problème, pas un mode d'emploi pour l'exploiter.

Le scénario est volontairement simple : une extension affiche, dans l'administration, un message de bienvenue personnalisé qui reprend le paramètre d'URL utilisé pour accéder à l'écran. C'est un schéma extrêmement courant dans les extensions qui affichent des notifications ou des filtres contextuels, et c'est précisément ce genre de fonctionnalité anodine qui cache le plus souvent une faille.

## Le code vulnérable

Voici, simplifié, le type de code que l'on peut retrouver dans l'écran d'administration d'une extension fictive :

```
function mon_plugin_afficher_ecran() {
    $onglet = $_GET['onglet'];
    echo '<h1>Bienvenue sur l\'onglet : ' . $onglet . '</h1>';
    // ... reste de l'affichage de l'écran
}
```

À première vue, ce code semble inoffensif : il affiche simplement le nom de l'onglet actif, une donnée qui provient de l'administrateur du site lui-même, dans son propre navigateur. C'est exactement ce raisonnement, « c'est moi qui contrôle cette URL, donc c'est sans danger », qui constitue l'erreur de fond.

## Pourquoi ce code est dangereux

Le paramètre `$_GET['onglet']` n'est pas une donnée que l'administrateur saisit lui-même dans la majorité des cas réels : c'est une valeur transmise dans l'URL, ce qui signifie qu'elle peut être construite par quelqu'un d'autre et transmise à la victime sous forme de lien. Un attaquant peut construire une URL du type :

```
https://exemple.fr/wp-admin/admin.php?page=mon-plugin&onglet=%22%3E%3Cscript%3Ealert(document.cookie)%3C/script%3E
```

Si un administrateur connecté clique sur ce lien, par exemple reçu dans un email de phishing ou déposé dans un commentaire, le code inséré dans le paramètre `onglet` s'exécute dans son navigateur, dans le contexte de session de l'administration WordPress. C'est une faille XSS réfléchie (*reflected XSS*) : la donnée malveillante n'est jamais stockée, elle transite directement de la requête à la réponse, mais l'impact reste tout aussi réel, car le script s'exécute avec les privilèges de la victime.

> L'essentiel à retenir : Une faille XSS naît presque toujours d'une donnée affichée sans échappement ; Le paramètre d'URL est le vecteur le plus fréquent dans les extensions ; Trois lignes de correction suffisent généralement à fermer la faille

## Comment la repérer en revue de code

Ce type de faille suit un schéma reconnaissable, ce qui permet de le repérer méthodiquement en revue de code plutôt que par hasard. Les points de vigilance à systématiser :

- Repérer chaque endroit où une superglobale (`$_GET`, `$_POST`, `$_REQUEST`, `$_COOKIE`, `$_SERVER`) est lue directement ;
- Suivre cette valeur jusqu'à son point d'affichage, même si plusieurs fonctions intermédiaires la manipulent au passage ;
- Vérifier si un `echo`, un `print` ou une concaténation dans une chaîne HTML utilise cette valeur sans passer par `esc_html()`, `esc_attr()` ou une fonction équivalente ;
- Ne jamais présumer qu'une donnée est « sûre » parce qu'elle semble provenir d'un contexte administrateur : c'est justement le contexte le plus intéressant pour un attaquant, puisque les privilèges y sont les plus élevés.

Un bon réflexe consiste à chercher dans le code source de l'extension toutes les occurrences de `$_GET`, `$_POST` et `$_REQUEST`, puis à vérifier pour chacune si la donnée finit par être affichée sans échappement. C'est fastidieux sur une grosse extension, mais c'est la méthode la plus fiable pour ne rien laisser passer.

## La correction

La correction de ce cas d'école tient en une seule fonction, appliquée au bon endroit :

```
function mon_plugin_afficher_ecran() {
    $onglet = isset( $_GET['onglet'] ) ? sanitize_key( wp_unslash( $_GET['onglet'] ) ) : 'general';
    echo '<h1>Bienvenue sur l\'onglet : ' . esc_html( $onglet ) . '</h1>';
}
```

Deux couches de protection travaillent ici ensemble : `sanitize_key()` à l'entrée, qui réduit la valeur à des minuscules, chiffres et tirets bas (un choix logique puisqu'un nom d'onglet n'a pas besoin d'autre chose), et `esc_html()` à la sortie, qui garantit que même si une valeur inattendue passait la première étape, elle ne pourrait jamais être interprétée comme du HTML actif. C'est le principe de défense en profondeur appliqué à l'échelle d'une seule ligne de code.

## Ce que ce cas d'école enseigne

Plusieurs leçons se dégagent de cet exemple, transposables à la quasi-totalité des failles XSS que l'on rencontre dans les extensions WordPress :

1. Une faille XSS ne demande presque jamais de code complexe pour exister : elle naît le plus souvent d'un simple oubli d'échappement sur une ligne anodine ;
2. Le contexte d'administration n'est pas une excuse pour relâcher la vigilance, il en est au contraire une raison supplémentaire d'être rigoureux, puisque les privilèges y sont plus élevés ;
3. Sanitiser à l'entrée et échapper à la sortie sont deux réflexes complémentaires qui, appliqués ensemble, ferment la quasi-totalité des scénarios d'exploitation de ce type ;
4. Une revue de code systématique des superglobales est plus fiable qu'une relecture rapide du fichier principal de l'extension.

> Sur chaque extension que je développe ou que j'audite, je fais un passage dédié où je liste toutes les occurrences de `$_GET`, `$_POST` et `$_REQUEST` avant même de relire la logique métier. C'est souvent l'étape la plus rentable en temps passé, tant les failles XSS s'y regroupent.

## En résumé

Ce cas d'école générique illustre un schéma extrêmement répandu : une donnée d'URL affichée sans échappement dans un écran d'administration. La correction est simple une fois le problème identifié, mais l'identifier demande une méthode, pas de la chance. Systématiser la traque des superglobales non échappées, à chaque revue de code, reste la meilleure protection contre ce type de faille.
