# esc_html contre esc_attr : l’échappement qui ne correspond pas au contexte

> Un échappement techniquement présent mais mal choisi laisse parfois passer une faille XSS discrète. Contextes mal appariés et bonne fonction pour chacun.

- Auteur : WordPress Développement
- Publié le : 2021-02-15
- Mis à jour le : 2021-02-15
- Catégorie : Extensions
- URL : https://www.wpmoderne.fr/extensions/esc-html-esc-attr-echappement-contexte/

## L’essentiel

- Un échappement présent n'est pas toujours un échappement correct
- Chaque contexte de sortie a sa propre fonction dédiée
- esc_attr échoue silencieusement dans un contenu HTML libre

`Warning: esc_attr() used where esc_html() was expected` — ce message n'existe pas dans WordPress, et c'est bien tout le problème : un mauvais choix d'échappement ne provoque aucune alerte. Le code s'exécute normalement, la page s'affiche, et la faille reste invisible tant que personne n'injecte volontairement une charge malveillante dans le bon champ.

L'échappement en sortie est la dernière ligne de défense contre les failles de type XSS (injection de script). Le principe est simple : ne jamais afficher une donnée provenant de l'utilisateur, d'une base de données ou d'une API tierce sans l'échapper au moment précis où elle est écrite dans le HTML. Mais un principe simple ne suffit pas si la fonction choisie ne correspond pas au contexte réel de sortie.

## Ce que chaque fonction attend réellement

`esc_html()` convertit les caractères spéciaux HTML (`<`, `>`, `&`) pour qu'une donnée s'affiche comme du texte brut à l'intérieur d'un élément, sans jamais être interprétée comme une balise. `esc_attr()` fait quelque chose de proche, mais avec un objectif différent : elle prépare une valeur pour qu'elle soit insérée à l'intérieur d'un attribut HTML, entre guillemets, en neutralisant en plus les guillemets eux-mêmes.

Le piège vient de leur ressemblance apparente. Les deux fonctions échappent des caractères proches, ce qui donne l'impression qu'elles sont interchangeables. Elles ne le sont pas : chacune est pensée pour un contexte de sortie précis, et l'inverser laisse passer des cas que l'autre fonction aurait bloqués.

> L'essentiel à retenir : Un échappement présent n'est pas toujours un échappement correct ; Chaque contexte de sortie a sa propre fonction dédiée ; esc_attr échoue silencieusement dans un contenu HTML libre

## Un exemple de contexte mal apparié

```
// Incorrect : esc_attr() utilisée pour un contenu affiché comme texte
echo '<p>' . esc_attr( $commentaire_utilisateur ) . '</p>';

// Correct pour ce contexte
echo '<p>' . esc_html( $commentaire_utilisateur ) . '</p>';
```

Dans ce premier cas, `esc_attr()` échappe correctement les guillemets, mais son comportement sur certains caractères diffère légèrement de celui attendu pour un contenu texte libre. Le résultat s'affiche presque toujours correctement à l'œil, ce qui rend l'erreur difficile à détecter en relecture visuelle — elle ne saute aux yeux que lors d'un audit de sécurité ciblé ou d'un test d'intrusion.

## L'inverse est tout aussi problématique

```
// Incorrect : esc_html() utilisée pour un attribut
echo '<input type="text" value="' . esc_html( $valeur ) . '">';

// Correct pour un attribut
echo '<input type="text" value="' . esc_attr( $valeur ) . '">';
```

Ici, le risque est plus direct : `esc_html()` n'échappe pas systématiquement tous les guillemets doubles de la même manière qu'`esc_attr()`, ce qui peut, selon la valeur injectée, permettre de sortir prématurément de l'attribut et d'ajouter un attribut supplémentaire, voire un gestionnaire d'événement, à l'élément HTML.

## Panorama des fonctions selon le contexte

| Contexte de sortie | Fonction correcte | Erreur fréquente |
| --- | --- | --- |
| Texte entre deux balises | `esc_html()` | Utiliser `esc_attr()` |
| Valeur d'un attribut HTML | `esc_attr()` | Utiliser `esc_html()` |
| URL dans un attribut `href` ou `src` | `esc_url()` | Utiliser `esc_attr()` seule |
| Bloc de JavaScript inline | `esc_js()` | Utiliser `esc_html()` |
| Contenu HTML riche autorisé | `wp_kses_post()` | Aucun échappement du tout |

## Ce qu'il faut retenir pour éviter l'erreur

La règle la plus fiable ne consiste pas à mémoriser des cas particuliers, mais à se poser une seule question à chaque ligne d'affichage : où, précisément, cette donnée atterrit-elle dans le HTML généré ? À l'intérieur d'une balise ouvrante et fermante, entre guillemets d'un attribut, dans une URL, ou dans un bloc de script. La réponse détermine la fonction, sans exception.

- Ne jamais choisir une fonction d'échappement par habitude, mais par contexte de sortie.
- Ne jamais réutiliser une valeur déjà échappée pour un autre contexte sans la ré-échapper correctement.
- Toujours tester avec une valeur contenant des guillemets et des chevrons, pas seulement du texte ordinaire.

> Un code qui échappe systématiquement, même mal, donne un faux sentiment de sécurité bien plus dangereux qu'une absence totale d'échappement, repérée immédiatement lors d'une relecture.

## En résumé

Un échappement mal apparié à son contexte reste, à l'œil, indiscernable d'un échappement correct. La seule protection réelle consiste à connaître précisément ce que chaque fonction native attend en entrée et où elle est censée être utilisée en sortie, puis à appliquer cette règle systématiquement, sans raccourci, sur chaque donnée affichée.
