Que sont les entités HTML ?
Les entités HTML sont des séquences de texte spéciales utilisées pour représenter des caractères qui ont soit une signification réservée en HTML, soit qui ne peuvent pas être facilement saisis ou transmis. Une entité commence par une esperluette (&) et se termine par un point-virgule (;). Entre ces délimiteurs se trouve soit un nom descriptif (une entité nommée comme &), soit un point de code numérique (une entité numérique comme & ou &).
À première vue, les entités semblent être un détail typographique mineur. En réalité, elles sont la pierre angulaire de la sécurité Web, de l'internationalisation et d'un rendu fiable par les navigateurs. Tout développeur Web qui travaille avec du contenu dynamique — saisies utilisateur, données CMS, modèles d'e-mails ou moteurs de modèles — doit comprendre comment et quand coder les entités HTML.
Une brève histoire des entités HTML et du codage des caractères
L'ère ASCII (années 1960–1980)
L'American Standard Code for Information Interchange (ASCII) définissait 128 caractères : les 26 lettres anglaises (majuscules et minuscules), les chiffres, la ponctuation et les codes de contrôle. C'était suffisant pour l'anglais américain, mais totalement inadéquat pour le reste des langues du monde.
Latin-1 / ISO-8859-1 (années 1980–1990)
L'ISO-8859-1 (également appelé Latin-1) a étendu l'ASCII à 256 caractères en utilisant le 8ème bit, ajoutant des caractères accentués utilisés dans les langues d'Europe occidentale (é, ü, ñ, etc.). HTML 2.0 et HTML 3.2 ont formellement adopté le Latin-1 comme jeu de caractères de référence, et les entités nommées HTML pour beaucoup de ces caractères ont été définies à cette époque — des entités comme é (é), ü (ü) et ñ (ñ).
Le problème : 256 caractères ne pouvaient toujours pas couvrir le japonais, l'arabe, le chinois, le coréen ou des centaines d'autres écritures. Différentes régions ont inventé des codages incompatibles (Shift-JIS, Big5, KOI8-R…), créant le problème "Mojibake" — du texte illisible lorsque les codages étaient mélangés.
Unicode et UTF-8 (de 1991 à aujourd'hui)
Le Consortium Unicode a publié sa première norme en 1991 dans le but d'attribuer un point de code unique à chaque caractère de chaque système d'écriture. Aujourd'hui, Unicode couvre plus de 140 000 caractères dans plus de 150 écritures.
L'UTF-8, introduit en 1992 par Ken Thompson et Rob Pike, code les points de code Unicode sur 1 à 4 octets et est rétrocompatible avec l'ASCII. Il est devenu le codage dominant pour le Web dans les années 2000. En 2024, plus de 98 % des pages Web utilisent l'UTF-8.
Pourquoi les entités comptent-elles toujours dans un monde UTF-8 ?
Si nous pouvons coder n'importe quel caractère avec l'UTF-8, pourquoi les entités existent-elles encore ? Pour trois raisons :
- Caractères réservés :
<,>, et&ont une signification spéciale dans le balisage HTML. Même dans les documents UTF-8, vous devez les échapper pour les afficher littéralement. - Délimiteurs d'attributs :
"et'délimitent les valeurs d'attributs et doivent être échappés à l'intérieur de ces valeurs. - Contrôle des espaces blancs :
(espace insécable) contrôle la mise en page de manière que les espaces ordinaires ne peuvent pas.
Concepts de base : comment fonctionnent les entités HTML
Entités nommées
Les entités nommées sont la forme la plus lisible par l'homme, utilisant un nom mnémonique dérivé de la description du caractère. HTML5 définit plus de 2 000 entités nommées.
<!-- Utilisation d'entités nommées -->
<p>Pain & Beurre</p> <!-- affiche : Pain & Beurre -->
<p>3 < 5 et 10 > 7</p> <!-- affiche : 3 < 5 et 10 > 7 -->
<p>Copyright © 2026</p> <!-- affiche : Copyright © 2026 -->
<p>Prix : 49€</p> <!-- affiche : Prix : 49€ -->
Entités numériques : décimales et hexadécimales
N'importe quel caractère Unicode peut être référencé par son point de code sous forme décimale ou hexadécimale :
- Décimal :
&#suivi du point de code décimal — ex. :<pour<(U+003C) - Hexadécimal :
&#xsuivi du point de code hexadécimal — ex. :<pour<
Les deux formes sont équivalentes. L'hexadécimal est courant dans la documentation technique car les points de code Unicode sont normalement exprimés en hexadécimal (U+003C).
<!-- Trois façons équivalentes d'afficher < -->
<
<
<
Les 5 entités de sécurité critiques
Ces cinq caractères constituent le fondement de la défense contre l'injection HTML :
| Caractère | Entité nommée | Décimal | Hex | Contexte |
|---|---|---|---|---|
< |
< |
< |
< |
Ouvre les balises HTML |
> |
> |
> |
> |
Ferme les balises HTML |
& |
& |
& |
& |
Commence les entités |
" |
" |
" |
" |
Attributs entre guillemets doubles |
' |
' |
' |
' |
Attributs entre guillemets simples |
Codez toujours ces cinq caractères lorsque vous réaffichez des saisies utilisateur en HTML.
Tableau de référence des entités HTML
| Caractère | Entité nommée | Décimal | Hex | Utilisation |
|---|---|---|---|---|
< |
< |
< |
< |
Délimiteurs de balises |
> |
> |
> |
> |
Délimiteurs de balises |
& |
& |
& |
& |
Préfixe d'entité |
" |
" |
" |
" |
Valeurs d'attributs |
' |
' |
' |
' |
Valeurs d'attributs |
|
|
  |
  |
Espace insécable |
© |
© |
© |
© |
Copyright |
® |
® |
® |
® |
Marque déposée |
™ |
™ |
™ |
™ |
Marque commerciale |
€ |
€ |
€ |
€ |
Signe Euro |
— |
— |
— |
— |
Tiret cadratin |
– |
– |
– |
– |
Tiret demi-cadratin |
Prévention XSS : pourquoi le codage sauve votre site
Le Cross-Site Scripting (XSS) est l'une des vulnérabilités de sécurité Web les plus répandues. Il se produit lorsqu'un attaquant injecte des scripts malveillants dans du contenu qui est ensuite servi à d'autres utilisateurs. Le codage des entités HTML est la défense principale.
L'attaque XSS classique
Considérez une fonction de recherche qui réaffiche la requête de l'utilisateur :
<!-- VULNÉRABLE : insertion directe de la saisie utilisateur -->
<p>Vous avez recherché : <?php echo $_GET['q']; ?></p>
Un attaquant conçoit une URL telle que :
https://example.com/search?q=<script>document.cookie</script>
Le navigateur affiche la balise <script> et exécute le code de l'attaquant. Avec document.cookie, ils volent les jetons de session. Avec fetch(), ils exfiltrent des données vers un serveur contrôlé par l'attaquant.
La solution : coder à la sortie
<!-- SÉCURISÉ : coder toute la sortie -->
<p>Vous avez recherché : <?php echo htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8'); ?></p>
Maintenant, le navigateur voit :
<p>Vous avez recherché : <script>document.cookie</script></p>
Le script est affiché comme du texte inoffensif — aucune exécution n'a lieu.
Exemples de code pratiques
JavaScript : manipulation sûre du DOM
La façon la plus sûre d'insérer du contenu généré par l'utilisateur en JavaScript est textContent, qui n'interprète jamais le HTML :
// SÉCURISÉ : textContent n'analyse jamais le HTML
const el = document.getElementById('output');
el.textContent = userInput; // échappe automatiquement tout
// DANGEREUX : innerHTML analyse et exécute le HTML
el.innerHTML = userInput; // Ne faites JAMAIS cela avec une saisie non fiable
Si vous devez construire des chaînes HTML en JavaScript, échappez-les toujours d'abord :
function escapeHtml(str) {
return str
.replace(/&/g, '&') // doit venir en premier
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const safe = `<p>Vous avez recherché : ${escapeHtml(userInput)}</p>`;
Remarque : échappez toujours & en premier — si vous échappez < en premier, le & dans < serait lui-même échappé en &lt;, provoquant un double codage.
PHP : htmlspecialchars() et htmlentities()
PHP fournit deux fonctions principales pour le codage HTML :
// htmlspecialchars : code uniquement les 5 caractères critiques
$safe = htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// htmlentities : code TOUS les caractères ayant un équivalent d'entité nommée
$safe = htmlentities($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
Différence clé : htmlspecialchars() ne code que <, >, &, " et '. htmlentities() code également les lettres accentuées et les symboles comme é → é. Pour les documents UTF-8, htmlspecialchars() est généralement préféré — l'UTF-8 peut représenter tous les caractères directement ; seuls les cinq dangereux ont besoin d'être échappés.
Passez toujours ENT_QUOTES pour coder les deux types de guillemets, et spécifiez toujours 'UTF-8' comme jeu de caractères.
Python : html.escape()
import html
# Échappement de base
safe = html.escape(user_input)
# Échappe aussi les guillemets simples (quote=True par défaut depuis Python 3.2)
safe = html.escape(user_input, quote=True)
# Exemple
user_input = '<script>alert("XSS")</script>'
print(html.escape(user_input))
# Sortie : <script>alert("XSS")</script>
Modèles HTML (Jinja2, Django, Handlebars)
La plupart des systèmes de modèles modernes s'échappent automatiquement par défaut :
<!-- Jinja2 / Django : auto-échappement par défaut -->
<p>{{ user_comment }}</p>
<!-- Rendu de HTML brut délibérément (DANGEREUX si contrôlé par l'utilisateur) : -->
<p>{{ user_comment | safe }}</p>
<!-- Handlebars : les doubles accolades échappent, les triples non -->
<p>{{userComment}}</p> <!-- échappé — sécurisé -->
<p>{{{userComment}}}</p> <!-- HTML brut — dangereux ! -->
Cas d'utilisation dans le monde réel
1. Documentation technique et blogs de code
Lorsque vous écrivez sur le HTML, vous devez fréquemment montrer des exemples de code contenant <, >, et &. Les entités vous permettent d'afficher ceux-ci comme des caractères littéraux sans casser la structure de la page :
<pre><code>
Utilisez <div> et </div> pour envelopper les sections.
Le caractère & commence une entité HTML.
</code></pre>
2. CMS et contenu généré par l'utilisateur
Tout CMS qui stocke et affiche du texte généré par l'utilisateur doit coder les entités HTML avant de l'afficher sur la page. Cela inclut les commentaires de blog, les messages de forum, les avis sur les produits et les messages sur les réseaux sociaux. L'omission de cette étape est responsable d'une grande partie des incidents XSS réels.
3. Modèles d'e-mails HTML
Les clients de messagerie sont notoirement incohérents. L'utilisation d'entités nommées pour les caractères typographiques (—, ‘, ’, …) aide à garantir un rendu correct sur Gmail, Outlook, Apple Mail et les clients hérités comme Outlook 2007 (qui utilise le moteur de rendu de Word).
4. Typographie et symboles spéciaux
Les entités offrent un accès fiable à des caractères typographiques qui sont difficiles à saisir ou qui peuvent ne pas survivre au copier-coller entre systèmes :
<p>Le tiret cadratin—utilisé pour les incises—est plus expressif qu'un trait d'union.</p>
<p>Elle a dit “bonjour” et a souri.</p>
<p>Prix : 29 €</p>
<!-- empêche "29" et "€" de se séparer sur des lignes différentes -->
5. Internationalisation dans les systèmes hérités
Dans les systèmes hérités qui ne peuvent pas gérer l'UTF-8 de manière fiable, les entités numériques permettent de coder n'importe quel caractère Unicode :
<!-- Caractère chinois pour "dragon" (U+9F99) en entité décimale -->
龙
<!-- Hiragana japonais あ (U+3042) -->
あ
Entités nommées vs numériques : comparaison
| Aspect | Nommée (<) |
Décimale (<) |
Hexadécimale (<) |
|---|---|---|---|
| Lisibilité | Haute | Moyenne | Basse |
| Couverture | ~2 000 car. | Tout Unicode | Tout Unicode |
| Support HTML5 | Complet | Complet | Complet |
| Support XML | Seuls 5 prédéfinis | Complet | Complet |
| Idéal pour | Caractères courants | N'importe quel Unicode | Réf. techniques/Unicode |
HTML vs XML : une différence critique
XML ne prédéfinit que 5 entités (<, >, &, ", '). Toutes les autres entités nommées comme © ou sont non définies en XML, sauf si elles sont déclarées dans une DTD.
<!-- INVALIDE en XML (entité non définie) : -->
<p>Copyright © 2026</p>
<!-- VALIDE en XML (l'entité numérique fonctionne partout) : -->
<p>Copyright © 2026</p>
<!-- VALIDE en HTML5 (les deux fonctionnent) : -->
<p>Copyright © 2026</p>
Si vous écrivez du XHTML ou du SVG, utilisez des entités numériques pour tout ce qui va au-delà des 5 de base, ou utilisez le caractère UTF-8 littéral directement.
Bonnes pratiques
1. Utilisez l'UTF-8 dans toute votre pile
Déclarez l'UTF-8 partout — le classement de la base de données, l'en-tête HTTP Content-Type et la balise HTML <meta charset>. Cela élimine le besoin de coder les caractères non ASCII avec des entités.
<meta charset="UTF-8">
header('Content-Type: text/html; charset=UTF-8');
2. Codez de manière appropriée au contexte
Différents contextes d'injection nécessitent différentes stratégies d'échappement :
- Corps HTML : coder
<,>,& - Attributs HTML : coder
<,>,&,",' - Chaînes JavaScript : utiliser l'échappement
\uXXXXou le codage JSON - Valeurs CSS : des règles d'échappement différentes s'appliquent
- URL : utiliser le codage en pourcentage (
%3Cet non<)
Un caractère codé pour un contexte n'est pas nécessairement sûr dans un autre.
3. Codez à la sortie, pas à l'entrée
Stockez les données brutes dans votre base de données. Codez lors de l'affichage en HTML. Si vous codez à l'entrée, vous risquez un double codage à la sortie, et les données deviennent corrompues dans les contextes non HTML (API JSON, e-mails en texte brut, etc.).
4. Ne décodez jamais une saisie non fiable avant le traitement
Décoder les entités fournies par l'utilisateur avant d'appliquer les filtres de sécurité va à l'encontre du but recherché. <script> décodé devient <script> — un contournement classique des filtres naïfs qui "bloquent les chevrons".
5. Évitez le double codage
Le double codage (&lt; qui s'affiche comme < et non <) est une erreur courante lorsque plusieurs couches d'application codent chacune indépendamment. Centralisez votre codage dans une seule couche de présentation.
6. Rappelez-vous que ' n'était pas dans le HTML4
L'entité ' est définie en XML et XHTML mais n'était pas définie en HTML4. Dans les environnements HTML4, utilisez ' à la place. HTML5 a officiellement ajouté ' à la liste des entités nommées.
Foire aux questions
Q : Dois-je coder chaque caractère spécial, ou seulement les plus dangereux ?
Pour la sécurité, vous devez coder au moins les 5 caractères critiques (< > & " '). Pour la typographie (signes de copyright, tirets, symboles monétaires), l'utilisation du caractère UTF-8 littéral dans un document UTF-8 est parfaitement acceptable. Les entités sont plus critiques dans les systèmes hérités ou lorsque le codage des caractères ne peut être garanti.
Q : Quelle est la différence entre & et & ?
& est le caractère esperluette littéral. & est sa représentation en entité HTML. Dans le code source HTML, chaque fois que vous voulez afficher un & littéral, vous devez écrire &. Si vous écrivez un & nu avant un mot, les navigateurs peuvent essayer de l'interpréter comme un début d'entité et l'afficher de manière incorrecte.
Q : Pourquoi se comporte-t-il différemment d'un espace ordinaire ?
Un espace ordinaire (U+0020) est un espace sécable — les navigateurs peuvent rompre les lignes à cet endroit, et plusieurs espaces consécutifs sont réduits à un seul. (espace insécable, U+00A0) empêche les sauts de ligne entre les caractères environnants et n'est pas réduit. Utile pour garder des valeurs comme "100 km" ou "M. Smith" sur une seule ligne.
Q : Puis-je utiliser des entités numériques pour les emoji ?
Oui. Les emoji ont des points de code Unicode et peuvent être représentés sous forme d'entités numériques. L'emoji 😀 (U+1F600) est 😀 en hexadécimal ou 😀 en décimal. Dans les documents UTF-8, vous pouvez coller l'emoji directement, mais les entités numériques fonctionnent comme une solution de secours fiable.
Q : Quel est le risque XSS spécifique aux attributs href ?
L'attribut href présente un danger unique : les URL peuvent utiliser le protocole javascript:. Le codage HTML seul n'est pas suffisant :
<!-- DANGEREUX même si < et > sont codés : -->
<a href="javascript:alert(1)">Cliquez ici</a>
<!-- Sûr : valider que href commence par http:// ou https:// -->
<?php
$url = $_GET['url'];
if (!preg_match('/^https?:\/\//i', $url)) {
$url = '#'; // rejeter les protocoles dangereux
}
echo '<a href="' . htmlspecialchars($url) . '">Lien</a>';
?>
Q : Est-il sûr d'utiliser innerHTML si je code le contenu d'abord ?
Si vous codez correctement les 5 caractères critiques avant de les assigner à innerHTML, c'est généralement sûr pour injecter du texte brut. Cependant, textContent est plus simple et plus infaillible. Réservez innerHTML aux cas où vous souhaitez intentionnellement insérer une structure HTML contrôlée.
Q : Les frameworks JavaScript modernes gèrent-ils automatiquement le codage HTML ?
Oui — React, Vue, Angular et Svelte échappent la sortie par défaut. Le JSX de React échappe automatiquement les valeurs interpolées avec {}. Cependant, chacun fournit un contournement explicite (React dangerouslySetInnerHTML, Vue v-html) qui doit être utilisé avec une extrême prudence et uniquement avec du contenu de confiance.
Résumé
Les entités HTML sont un mécanisme essentiel pour :
- La sécurité — neutraliser
<,>,&,", et'pour prévenir l'injection XSS - L'exactitude — garantir que les caractères ayant une signification réservée en HTML sont affichés littéralement
- La compatibilité — représenter n'importe quel caractère Unicode dans des environnements hérités ou restreints
- La typographie — insérer des tirets cadratins, des espaces insécables, des symboles monétaires et d'autres caractères spéciaux de manière fiable
Dans une pile UTF-8 moderne, vous avez principalement besoin de coder les 5 caractères critiques pour la sécurité lors de l'affichage de contenu dynamique en HTML. Les entités nommées comme et — restent utiles pour la typographie. Comprendre la différence entre les entités nommées et numériques, ainsi que la divergence entre les règles HTML et XML, fera de vous un développeur Web plus efficace et plus soucieux de la sécurité.