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

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_htmlavec un script simple parcourantwp_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.