# Échapper ses sorties dans WordPress : esc_html, esc_attr et wp_kses expliqués

> Le principe « échapper tard » évite la majorité des failles XSS. Voici quand utiliser esc_html, esc_attr, esc_url, wp_kses et wp_kses_post dans un template.

- Auteur : Clément Hadrot
- Publié le : 2021-03-23
- Mis à jour le : 2021-03-23
- Catégorie : Sécurité
- URL : https://wpmoderne.dev.wordpress-developpement.fr/securite/echapper-sorties-esc-html-attr-kses/

## L’essentiel

- Échapper tard, au moment de l'affichage, jamais avant
- Chaque contexte HTML a sa fonction d'échappement dédiée
- wp_kses_post autorise le HTML de confiance, wp_kses le HTML personnalisé restreint

La faille XSS (Cross-Site Scripting) est, avec l'injection SQL, l'une des vulnérabilités les plus courantes dans l'écosystème WordPress. Elle survient quand une donnée non fiable, saisie par un utilisateur ou provenant d'une source externe, se retrouve affichée telle quelle dans une page HTML, permettant à un attaquant d'y injecter du code JavaScript exécuté dans le navigateur d'une victime. La bonne nouvelle, c'est que WordPress fournit une famille de fonctions d'échappement simples à utiliser, à condition de connaître le bon réflexe pour chaque contexte.

Le principe qui guide tout le reste s'appelle « échapper tard » (*escape late*) : on n'échappe jamais une donnée au moment où on la stocke en base ou qu'on la manipule, mais uniquement au tout dernier moment, juste avant de l'afficher dans le HTML. Cela évite d'accumuler des couches d'échappement successives qui finissent par corrompre la donnée elle-même.

## esc_html et esc_attr : les deux réflexes de base

`esc_html()` convertit les caractères spéciaux HTML (`<`, `>`, `&`...) en entités, pour qu'un texte affiché entre deux balises ne puisse jamais être interprété comme du code :

```
<h1><?php echo esc_html( $titre_utilisateur ); ?></h1>
```

`esc_attr()` fait un travail similaire, mais adapté au contexte d'un attribut HTML, où les guillemets doivent être échappés pour éviter qu'une donnée ne « sorte » de l'attribut et n'injecte un nouvel attribut ou un gestionnaire d'événement :

```
<input type="text" value="<?php echo esc_attr( $valeur ); ?>" />
```

Une confusion fréquente consiste à utiliser `esc_html()` dans un attribut ou inversement. Le résultat est souvent visuellement correct, ce qui masque le problème, mais certains caractères ne sont pas traités de la même façon selon la fonction, ce qui peut laisser une brèche exploitable selon le contexte exact.

## esc_url pour les liens et redirections

`esc_url()` nettoie une URL avant affichage, en retirant les caractères invalides et en bloquant les protocoles dangereux comme `javascript:`, qui permettrait d'exécuter du script au clic sur un lien :

```
<a href="<?php echo esc_url( $lien_utilisateur ); ?>">Voir plus</a>
```

Pour une redirection PHP, la variante `esc_url_raw()` est plus adaptée, car elle n'encode pas les esperluettes en entités HTML, ce qui casserait une URL utilisée hors d'un contexte d'affichage :

```
wp_safe_redirect( esc_url_raw( $url_destination ) );
exit;
```

> L'essentiel à retenir : Échapper tard, au moment de l'affichage, jamais avant ; Chaque contexte HTML a sa fonction d'échappement dédiée ; wp_kses_post autorise le HTML de confiance, wp_kses le HTML personnalisé restreint

## wp_kses et wp_kses_post pour le HTML autorisé

Parfois, une donnée doit conserver un minimum de balisage HTML à l'affichage, par exemple un champ de description qui autorise du gras ou des liens. Dans ce cas, `esc_html()` est trop strict puisqu'il échapperait aussi les balises légitimes. C'est là qu'interviennent `wp_kses()` et `wp_kses_post()`.

`wp_kses_post()` applique le même filtre que celui utilisé pour le contenu des articles : il autorise les balises et attributs courants d'un article WordPress (paragraphes, liens, gras, italique, listes...) et retire tout le reste, y compris les scripts :

```
echo wp_kses_post( $description_utilisateur );
```

`wp_kses()`, plus flexible, accepte en second paramètre un tableau définissant précisément les balises et attributs autorisés, pour un contrôle plus fin que `wp_kses_post()` :

```
$balises_autorisees = array(
    'strong' => array(),
    'em'     => array(),
    'a'      => array(
        'href'  => array(),
        'title' => array(),
    ),
);

echo wp_kses( $texte_utilisateur, $balises_autorisees );
```

Cette approche est particulièrement utile dans une extension qui affiche un champ de texte enrichi limité, sans vouloir autoriser tout le balisage disponible pour le contenu d'un article.

## Choisir la bonne fonction selon le contexte

Un résumé pratique des cas les plus courants rencontrés dans un template :

- Texte simple affiché entre balises : `esc_html()` ;
- Valeur insérée dans un attribut HTML : `esc_attr()` ;
- URL affichée dans un `href` ou `src` : `esc_url()` ;
- URL utilisée pour une redirection PHP : `esc_url_raw()` ;
- Contenu HTML limité provenant d'un utilisateur de confiance (auteur, éditeur) : `wp_kses_post()` ou `wp_kses()` avec une liste de balises précise ;
- Donnée insérée dans un bloc `<script>` JavaScript : `wp_json_encode()`, une fonction à part qui mérite un traitement dédié tant les pièges y sont différents.

## Une erreur fréquente : échapper trop tôt

Un piège classique consiste à appeler `esc_html()` ou `esc_attr()` au moment de l'enregistrement en base de données plutôt qu'à l'affichage. Le résultat, ce sont des entités HTML doublement encodées qui s'affichent littéralement à l'écran (par exemple `&amp;` au lieu de `&`), un problème que je retrouve régulièrement en audit sur des extensions anciennes. La règle reste constante : on sanitize à l'entrée pour la validité de la donnée, on échappe à la sortie pour la sécurité de l'affichage, jamais l'inverse et jamais les deux ensemble sur la même étape.

> Je conseille à tous les développeurs que j'accompagne de bannir purement et simplement le simple `echo $variable;` de leurs templates, sauf pour une valeur qu'ils ont eux-mêmes générée en PHP et qui ne contient jamais d'entrée utilisateur. C'est un réflexe qui prend une semaine à s'installer et qui évite ensuite la quasi-totalité des failles XSS basiques.

## En résumé

Échapper ses sorties n'est pas une option facultative réservée aux gros projets : c'est un réflexe de base à appliquer sur chaque variable affichée dans un template WordPress, quelle que soit sa source apparente. Retenez le triptyque `esc_html` / `esc_attr` / `esc_url` pour la majorité des cas, et `wp_kses_post` ou `wp_kses` dès qu'un minimum de HTML doit être conservé. Le principe « échapper tard » reste la boussole à suivre dans tous les cas de figure.
