# unfiltered_html accordé à tous les rédacteurs d’un site de presse : l’erreur révélée par un audit

> Un audit de sécurité révèle qu'une capacité normalement réservée aux administrateurs a été accordée par confort à toute une rédaction. Analyse et correction.

- Auteur : Clément Hadrot
- Publié le : 2021-04-27
- Mis à jour le : 2021-04-27
- Catégorie : Sécurité
- URL : https://wpmoderne.dev.wordpress-developpement.fr/securite/unfiltered-html-redacteurs-presse-erreur/

## L’essentiel

- unfiltered_html contourne toutes les protections d'échappement
- Le confort d'écriture ne justifie pas une capacité aussi large
- Un éditeur de blocs personnalisé règle le vrai besoin

Vingt-trois comptes. C'est le nombre de comptes au rôle « rédacteur » disposant de la capacité `unfiltered_html` découverts lors d'un audit de sécurité mené sur le site d'un média en ligne, alors que cette capacité n'est, par défaut, jamais accordée à ce rôle dans WordPress. Elle est réservée, en configuration standard d'un site simple, aux seuls administrateurs, précisément parce qu'elle désactive le filtrage du HTML saisi dans le contenu.

L'origine de cette dérive était connue de l'équipe technique, sans que personne n'ait mesuré sa portée réelle : deux ans plus tôt, des rédacteurs s'étaient plaints de ne pas pouvoir insérer certaines balises HTML précises dans leurs articles (des iframes de tweets embarqués, du code d'intégration fourni par des partenaires publicitaires). Plutôt que de traiter chaque cas d'usage individuellement, un développeur pressé avait ajouté `unfiltered_html` au rôle `author` tout entier, réglant le symptôme sans traiter la cause.

## Ce que unfiltered_html désactive réellement

Par défaut, WordPress passe le contenu des articles à travers `wp_kses_post()` au moment de l'enregistrement, qui autorise un ensemble défini de balises et d'attributs tout en supprimant le reste, notamment les balises `<script>` ou les attributs `onerror`, `onload` et consorts. La capacité `unfiltered_html` retire complètement ce filtre pour l'utilisateur qui la possède : tout HTML saisi, y compris du JavaScript exécutable, est enregistré tel quel et affiché tel quel aux visiteurs du site.

Sur un site de presse où vingt-trois comptes disposaient de cette capacité, cela signifie que vingt-trois points d'entrée potentiels pouvaient publier du script exécuté par tous les visiteurs du site, sans qu'aucun mécanisme d'échappement des sorties (déjà largement documenté par ailleurs) ne puisse s'y opposer : l'échappement en sortie ne protège que ce qui a été filtré en amont, il ne rattrape jamais un contenu explicitement autorisé à contourner le filtrage.

## Pourquoi ce n'est pas qu'un risque théorique sur un site de presse

> L'essentiel à retenir : unfiltered_html contourne toutes les protections d'échappement ; Le confort d'écriture ne justifie pas une capacité aussi large ; Un éditeur de blocs personnalisé règle le vrai besoin

Un compte rédacteur compromis (mot de passe réutilisé, session volée sur un réseau public) devient, avec cette capacité, un vecteur direct d'attaque XSS stockée touchant l'ensemble des lecteurs du site, pas seulement l'espace d'administration. Sur un média à fort trafic, cela représente une audience considérable exposée en une seule publication d'article.

## Retirer la capacité sans bloquer les usages légitimes

```
$role = get_role( 'author' );
$role->remove_cap( 'unfiltered_html' );
```

Cette ligne suffit à retirer la capacité du rôle. Mais elle ne résout pas le besoin d'origine : intégrer des tweets, des vidéos ou du code publicitaire fourni par des partenaires. C'est là que la solution mérite d'être repensée plutôt que reconduite.

### Un bloc personnalisé plutôt qu'un accès HTML libre

Un bloc Gutenberg personnalisé, dédié à l'intégration d'oEmbed ou de code publicitaire whitelisté, répond au besoin réel sans exiger `unfiltered_html`. Ce bloc valide côté serveur, via `register_block_type()` et une fonction de rendu qui vérifie l'origine du contenu embarqué (domaine autorisé, structure attendue), que ce qui est inséré correspond à un format connu, plutôt que d'autoriser n'importe quel fragment HTML.

## Réserver unfiltered_html aux seuls cas qui le justifient réellement

Dans de très rares configurations (un site multi-utilisateurs où WordPress juge, via la constante `DISALLOW_UNFILTERED_HTML`, que même les administrateurs ne devraient pas en disposer par défaut), cette capacité mérite d'être traitée comme une exception documentée, jamais comme un réglage de confort accordé à un rôle entier.

> Une capacité qui désactive un mécanisme de sécurité ne se distribue jamais à un rôle pour résoudre un inconfort ponctuel : elle se réserve à des exceptions individuelles, documentées et révisées régulièrement.

## Ce qu'il faut vérifier sur son propre site

- Lister les rôles disposant de `unfiltered_html` avec un script simple parcourant `wp_roles()`.
- Vérifier que seuls les administrateurs, voire personne, ne la possèdent.
- Remplacer les usages légitimes identifiés par des blocs dédiés et validés, plutôt que par un accès HTML libre.

## En résumé

Accorder `unfiltered_html` à un rôle entier pour résoudre un besoin d'intégration ponctuel revient à désactiver une protection fondamentale pour tous les comptes concernés. Le bon réflexe consiste à traiter le besoin d'intégration par un composant dédié, jamais par un contournement global du filtrage.
