Protection WordPress : améliorer la sécurité grâce aux en-têtes Content Security Policy

Sur un site WordPress, la sécurité ne se résume pas à “installer un plugin” puis cocher deux cases. Le risque vient souvent de l’endroit le moins visible: le navigateur du visiteur, qui exécute du JavaScript, charge des ressources externes, et parfois interprète du contenu injecté par une faille ou par une mauvaise configuration. Dans ce contexte, les en-têtes HTTP jouent un rôle concret, parce qu’ils imposent des règles avant même que le navigateur ne fasse quoi que ce soit.

Parmi eux, Content Security Policy (CSP) mérite une place centrale dans une stratégie de protection WordPress. Une bonne CSP ne “corrige” pas une faille côté serveur, mais elle limite sévèrement l’impact quand quelque chose se passe mal. Elle dit, en clair, d’où le navigateur a le droit de charger scripts, feuilles de style, images, polices, frames, et comment il doit gérer certaines formes de code.

Je parle ici d’expérience terrain, pas de théorie. J’ai vu des sites qui étaient “corrects” en surface, mais qui laissaient le navigateur trop libre. Résultat, une simple injection de script dans un champ non filtré, ou une surcharge de balises via un thème un peu trop permissif, finissait en exécution. Une CSP bien construite n’élimine pas le problème initial, mais empêche le navigateur de faire la fête avec du JavaScript injecté.

CSP, c’est quoi exactement, et pourquoi ça change la donne

Content Security Policy est un en-tête que l’on envoie au navigateur. Le navigateur s’en sert comme d’un garde-fou. Au lieu de laisser le site charger n’importe quelle ressource depuis n’importe quelle origine, la CSP décrit explicitement les sources autorisées pour chaque type de contenu.

Concrètement, une CSP peut contenir des directives comme script-src, style-src, img-src, connect-src, font-src ou encore frame-ancestors. Selon la directive, elle s’applique à un ensemble de comportements du navigateur.

Le point important, c’est que CSP vise surtout un scénario: empêcher le chargement ou l’exécution de code non autorisé. Dans l’univers WordPress, cela se traduit souvent par deux catégories de risques:

image

Des scripts qui se chargent depuis des domaines non maîtrisés (publicités, analytics, tags marketing mal gérés, scripts hérités d’un thème). Du code injecté via une faille applicative, un plugin, un champ oublié, ou un accès admin compromis.

Une CSP robuste réduit drastiquement les conséquences du second point, même quand le premier n’est pas totalement sous contrôle.

Les vrais objectifs d’une CSP sur WordPress

Il faut être pragmatique. Sur un site WordPress, l’enjeu n’est pas d’écrire une CSP “parfaite” du premier coup. L’enjeu est d’avancer vers une politique restrictive qui reste compatible avec votre pile réelle: thème, plugins, widgets, scripts tiers, CDN, cache, intégrations.

Pour que ça serve réellement à la sécurité, je vise trois objectifs:

D’abord, empêcher l’exécution de JavaScript injecté. Cela passe par script-src et, très souvent, en évitant unsafe-inline.

Ensuite, réduire les points de fuite côté chargement externe. Un script qui peut venir de partout est un script qui vous échappe.

Enfin, limiter les comportements de framing, pour réduire les risques de clickjacking, via frame-ancestors.

Il y a aussi un avantage “hygiène” qui surprend: CSP révèle rapidement ce qui dépend de l’exécution inline ou de scripts “dynamiques”. Quand on corrige ces dépendances, on améliore aussi la maintenabilité.

Comprendre la politique: un exemple de base (et ses limites)

Une CSP minimale sur un site WordPress peut ressembler à ceci, en version simplifiée:

default-src 'self'; script-src 'self' https://example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;

Déjà, on voit les idées:

    default-src 'self' dit “par défaut, seulement depuis le même domaine”. script-src autorise le navigateur à charger des scripts depuis self et depuis un domaine externe précis. style-src autorise unsafe-inline, ce qui est souvent utile au début, parce que WordPress et certains thèmes injectent des styles inline. img-src autorise les images du même domaine et data: pour des cas comme certaines icônes ou images encodées.

Mais c’est là que le jugement compte. Mettre unsafe-inline dans script-src est généralement une très mauvaise idée si votre objectif est la résistance à l’injection. À l’inverse, unsafe-inline dans style-src peut être un compromis plus acceptable, surtout si vous ne pouvez pas tout corriger tout de suite.

Le vrai travail consiste à réduire progressivement les autorisations tout en gardant le site fonctionnel.

“Report-only” d’abord: la méthode la plus sûre pour déployer

Sur WordPress, la première CSP “qui casse tout” est souvent le résultat le plus frustrant. Parce que les plugins ne sont pas tous identiques, certains injectent des scripts ou des styles inline, d’autres chargent des ressources de domaines qu’on ne voit pas au premier regard.

La meilleure approche consiste à déployer une CSP en mode observation, via Content-Security-Policy-Report-Only. Le navigateur ne bloque pas, mais il envoie des rapports quand une ressource violerait la politique.

Dans la pratique, vous obtenez un signal: quelles directives sont trop restrictives, quelles origines doivent être ajoutées, et surtout quels comportements inline reviennent sans arrêt.

Ensuite seulement, vous basculez en politique active, Content-Security-Policy, quand le site se stabilise.

Cette stratégie réduit le risque d’un “blackout” sur des pages critiques comme la connexion, l’espace admin, ou des pages de checkout quand vous utilisez un plugin e-commerce.

Les pièges typiques sur WordPress

Je vois souvent les mêmes causes qui empêchent d’obtenir une CSP propre, ou qui donnent une impression de sécurité sans l’être.

1) Le script inline “par défaut”

Beaucoup de thèmes et plugins finissent par exécuter des scripts inline. Découvrir plus Même s’ils sont “sûrs” dans l’intention du développeur, une CSP qui autorise l’inline via unsafe-inline rend la politique beaucoup moins utile contre l’injection.

image

La solution technique existe, mais elle demande de l’alignement avec le code: utiliser des nonces (nonce-...) ou des hashes (sha256-...). Ce sont des mécanismes plus exigeants à mettre en place, mais ils permettent de dire “ce inline est autorisé, pas n’importe quel inline”.

2) Trop de domaines tiers autorisés

Un script-src trop permissif finit par devenir un laissez-passer. Si vous mettez des domaines “de confort” parce que “ça marche chez nous”, vous pouvez perdre une partie du gain de sécurité.

Le bon réflexe consiste à identifier les vrais domaines nécessaires. Pour ça, les rapports CSP sont précieux, mais il faut aussi valider côté code en inspectant les balises générées.

3) Les polices, les images et les balises cachées

CSP ne concerne pas que les scripts. Un thème qui charge une police depuis un CDN ou des images depuis data: peut déclencher des violations. Si vous bloquez font-src ou img-src trop strictement, vous aurez des pages dégradées: polices invisibles, icônes manquantes, images fantômes.

En mode observation, vous verrez immédiatement ce qui casse et vous pourrez ajuster les directives.

4) Les iframes et le clickjacking

Les protections contre le framing sont souvent absentes ou mal gérées. Une directive frame-ancestors protège contre le clickjacking, mais certains widgets tiers, comme des embeds vidéo ou des cartes, demandent des ajustements.

Sur WordPress, il faut distinguer le besoin d’afficher un contenu depuis un fournisseur, et le besoin de ne pas être encadré par des sites tiers non désirés.

Une checklist pratique avant d’écrire la CSP

Avant de coder l’en-tête, j’aime stabiliser le terrain. La CSP est comme une clôture: si vous changez le jardin pendant la construction, vous devez refaire des morceaux.

Voici une checklist courte, qui évite les erreurs classiques.

Identifier les sources externes réellement utilisées: analytics, tag manager, recaptcha, widgets, CDN de polices. Démarrer avec Content-Security-Policy-Report-Only et collecter les violations sur plusieurs pages (home, article, page de formulaire, connexion). Vérifier le rendu et la console navigateur quand une ressource est refusée, sans “sur-corriger” au premier blocage. Ne pas autoriser unsafe-inline pour script-src si votre objectif est la protection contre l’injection de code. Prévoir un plan de rollback au cas où la politique active casserait une page critique (rollback temporaire, ou retrait de la directive fautive).

Cette préparation a l’air “bureaucratique”, mais elle économise des heures de va-et-vient quand un plugin injecte un script de façon conditionnelle.

Définir une CSP utile pour WordPress, sans tuer la compatibilité

Il faut accepter une réalité: WordPress est un écosystème. Les thèmes et plugins modifient le HTML, les templates et souvent les assets chargés. Une CSP très stricte dès le départ vous oblige à maîtriser tout le front, plugin par plugin. C’est faisable, mais rarement immédiat.

Je procède par couches.

image

D’abord, je verrouille default-src et je définis des directives claires pour les catégories sensibles.

Ensuite, je traite script-src en priorité. C’est la directive la plus impliquée dans l’exploitation.

Enfin, je peaufine style-src, img-src, font-src, connect-src et frame-ancestors.

Pourquoi ce rythme? Parce que l’attaque la plus rentable cible généralement l’exécution de code ou la capacité à charger des ressources malicieuses. Une fois que l’essentiel est bloqué, on peut rendre la politique plus stricte sur le reste sans paniquer.

frame-ancestors: la protection souvent oubliée

Beaucoup d’administrateurs ajoutent des règles côté scripts, sans regarder ce qui encadre les pages. Pourtant, le clickjacking repose précisément sur le fait qu’un site malveillant puisse charger votre page dans un iframe et superposer des éléments cliquables.

La directive frame-ancestors indique aux navigateurs qui a le droit d’intégrer votre site dans un frame. Cette configuration est très utile pour des sites WordPress avec une interface d’administration ou des pages de formulaire.

Le compromis se fait rarement sur WordPress classique, sauf si vous vous auto-intégrez dans d’autres environnements (intranet, widgets dédiés) ou si vous utilisez des embeds très particuliers.

Nonce et hash: quand vous voulez vraiment aller plus loin

Sur une CSP “mature”, l’objectif est d’éviter unsafe-inline. Deux approches dominent:

    Les nonces: le serveur génère une valeur unique par requête, puis la CSP autorise l’exécution inline uniquement avec ce nonce. Les hashes: la CSP autorise des contenus inline dont le contenu correspond à un hash précis.

Dans WordPress, cette exigence est difficile à appliquer à l’ensemble des plugins sans intervention. Certains plugins injectent du inline sans exposer un mécanisme de nonce. D’autres écrivent des scripts qui changent selon le contexte.

C’est pour ça que, dans une démarche réaliste, on commence souvent par un report-only, puis on ajuste pour réduire les violations, et seulement ensuite on traite les scripts inline persistants. Parfois, on finit par remplacer un plugin par une solution plus propre côté front, simplement parce que la CSP a mis en évidence une dette technique.

Je ne recommande pas de forcer nonce partout si vous n’avez pas la capacité d’aligner vos thèmes et vos plugins. Une CSP trop ambitieuse, mais incomplète, peut finir contournée par des exceptions globales.

Utiliser les rapports CSP pour corriger, pas pour empiler des exceptions

Les rapports CSP ont une valeur double. Ils indiquent les refus exacts, et ils vous montrent vos priorités.

Le piège, c’est de répondre à chaque violation en ajoutant un domaine générique ou une règle trop permissive. Le résultat, c’est une politique qui ressemble à une “liste de courses” au lieu d’une architecture de sécurité.

Mon approche consiste à trier les violations:

    Celles causées par un domaine réellement nécessaire (analytics, captcha, CDN). Celles causées par du code que vous pouvez corriger (script inline non maîtrisé). Celles causées par du chargement inattendu (un plugin qui rajoute une origine non documentée).

Quand une violation révèle un comportement inattendu, je commence par auditer le HTML généré, puis je trace quel plugin le produit. Si le plugin n’est pas indispensable, le plus rentable est parfois de le supprimer.

Où configurer la CSP sur WordPress

Le “comment” dépend de votre stack:

    Au niveau du serveur web (Nginx, Apache), via une configuration de virtual host. Via un reverse proxy, si vous avez un front comme Cloudflare ou un équilibreur. Via des mécanismes WordPress, dans certains cas via un plugin de sécurité ou un code dans le thème, mais ce n’est pas toujours idéal, parce que vous voulez une CSP cohérente et non contournable.

Dans les environnements sérieux, je préfère une configuration au niveau web server ou proxy. Pourquoi? Parce que CSP est un en-tête sensible, et vous voulez éviter des situations où la politique n’est pas injectée sur certaines routes, ou pas sur l’admin, ou pire, uniquement quand un plugin est actif.

Si vous utilisez un cache agressif, vérifiez aussi que la CSP ne se retrouve pas “figée” dans une variante que vous n’attendez pas.

Cas concrets: ce qui casse souvent et comment réagir

Voici deux scénarios fréquents que j’ai rencontrés.

Cas 1: le formulaire WordPress cesse de fonctionner

Sur certains formulaires, une librairie JavaScript est chargée depuis une origine tierce ou un script inline initialisait un composant. CSP bloque ce script, donc le formulaire devient muet.

Réaction pratique: repérer, dans les rapports CSP, la directive violée (souvent script-src ou connect-src), puis identifier la ressource précise (URL, inline, événement). Si c’est un script de dépendance, vous l’autorisez depuis la bonne origine, pas depuis “wildcard”. Si c’est un inline, vous cherchez à le remplacer par un fichier, ou vous utilisez un nonce si c’est techniquement possible.

Cas 2: les images et les polices disparaissent partiellement

Sur des sites avec des thèmes qui utilisent des polices depuis des CDN, font-src bloque et vous obtenez un texte invisible ou reformaté. Sur des icônes encodées, img-src bloque data: et les éléments manquent.

Réaction pratique: ajuster font-src et img-src selon les origines réellement utilisées. Là aussi, l’observation est précieuse, parce que la page home n’est pas forcément représentative: une page d’article, un modèle de galerie, ou un popup peuvent déclencher d’autres ressources.

Exemple de plan de déploiement réaliste sur une base WordPress

Le déploiement “propre” ressemble rarement à un grand saut. Il ressemble plutôt à une séquence en plusieurs passes.

D’abord, je mets Content-Security-Policy-Report-Only avec une politique qui couvre les directives les plus sensibles, tout en restant compatible. Je laisse tourner suffisamment longtemps pour couvrir la diversité des pages et des comportements, comme les pages qui affichent du contenu chargé dynamiquement.

Ensuite, je bascule progressivement la politique active en retirant les permissifs que je peux remplacer. Le cœur du travail est souvent script-src, parce que c’est là que la tentation de mettre unsafe-inline se présente.

Enfin, je resserre progressivement les directives moins critiques, comme img-src et font-src, en évitant le “blocus total” qui dégrade l’UX.

Deux règles d’or pour éviter de rendre votre site fragile

On peut facilement se tromper, surtout quand CSP bloque des fonctionnalités dont on ne connaît pas l’origine. Deux règles m’ont évité des incidents:

    Toujours tester sur des pages qui déclenchent les scripts et les ressources spécifiques. Une home “propre” ne garantit rien. Ne changez pas dix choses en même temps. Chaque ajustement doit être interprétable. Sinon, quand un plugin tombe en panne, vous ne savez pas quelle correction a causé le problème.

Les limites de CSP, et pourquoi elle doit rester complémentaire

CSP est un excellent en-tête de protection. Mais ce n’est pas un bouclier magique.

Si un attaquant peut réussir un vol de session via une faiblesse autre que l’exécution de code, CSP ne suffira pas. Si un site autorise des actions sensibles sans contrôle d’accès ou sans nonce côté WordPress, CSP ne corrige pas ça. Si un plugin a une faille de type SSRF côté serveur, CSP ne peut pas la bloquer.

Ce que CSP fait très bien, c’est réduire le champ d’action d’une injection de contenu ou d’un chargement de script non autorisé dans le navigateur. Pour le reste, CSP s’ajoute à une base solide: mises à jour WordPress, plugins à jour, durcissement admin, et contrôle des droits.

Déboguer quand ça ne marche pas: méthode courte

Quand quelque chose casse, je préfère une méthode courte et répétable plutôt que de “bricoler” la CSP.

Consulter les violations CSP dans les DevTools, puis relever la directive et la ressource exacte refusée. Reproduire sur la page où ça casse, avec un cache désactivé si nécessaire. Corriger la source côté code ou ajuster uniquement l’origine nécessaire, pour éviter l’autorisation globale. Revenir en mode Report-Only si vous devez explorer sans impacter les utilisateurs. Re tester après chaque changement, page par page, pas seulement sur la home.

Cette discipline rend les corrections factuelles, pas émotionnelles.

Et côté “protection WordPress”, ça apporte quoi concrètement

Quand CSP est bien menée, vous obtenez plusieurs bénéfices tangibles:

    Une réduction de l’exécution de JavaScript injecté, donc moins d’impact sur les scénarios XSS. Une surface d’attaque réduite en matière de chargement de ressources. Une meilleure maîtrise des dépendances front, parce que vous voyez exactement d’où viennent vos scripts et styles.

Et surtout, CSP vous force à clarifier ce qui est nécessaire. Dans WordPress, cette clarté est une forme de sécurité indirecte. Moins de scripts inutiles, moins d’exceptions, moins de comportements “au hasard”.

Conclusion ouverte sur la pratique: commencer petit, puis resserrer

Je terminerai sur un point de méthode, plus que sur une promesse. La CSP n’est pas un projet “one shot”. C’est une démarche progressive, et c’est normal. Les sites WordPress ont des histoires, des plugins hérités, des réglages de thème, des intégrations marketing.

Si vous commencez en mode observation, si vous corrigez ce qui est corrigeable et si vous refusez les exceptions trop larges pour les scripts, vous obtenez un renforcement de sécurité réel. Et au fil du temps, CSP devient moins un frein, plus une confirmation: votre site charge ce qu’il doit charger, et le reste est refusé.

Si vous voulez, je peux aussi vous proposer une CSP de départ adaptée à votre configuration, à condition que vous me décriviez votre stack: serveur ou reverse proxy, plugins principaux qui injectent du JS, présence de Cloudflare, et domaines tiers utilisés.