Une politique de sécurité de contenu déclare au navigateur quelles ressources il a le droit de charger et d'exécuter sur une page. Correctement posée, elle rend inopérante la majorité des injections de code, y compris celles qui exploitent une faille que vous n'avez pas encore corrigée. Mal posée, elle bloque les polices, casse les formulaires et fait disparaître les cartes, sans message compréhensible pour le visiteur. La différence entre les deux résultats ne tient pas à la connaissance de la syntaxe mais à la méthode de déploiement, qui doit être progressive et instrumentée. La définition du mécanisme est rappelée dans notre article sur le CSP et la définition du Content Security Policy.
Ce que la politique protège réellement
Il faut d'abord cadrer ce que l'on gagne, parce que l'effort de déploiement est réel et qu'il doit être justifié. La protection principale porte sur l'exécution de code injecté dans la page, scénario dans lequel un attaquant parvient à faire écrire du script dans votre HTML par un chemin quelconque : commentaire non filtré, paramètre d'adresse réfléchi, contenu importé mal nettoyé. Sans politique, ce script s'exécute avec tous les droits de votre page, ce qui permet de voler des sessions, de rediriger des paiements ou d'exfiltrer des données de formulaire. Le mécanisme de cette famille d'attaques est détaillé dans notre article sur le XSS et le fonctionnement du Cross-Site Scripting.
Avec une politique stricte, ce même script n'est tout simplement pas exécuté, le navigateur refusant tout code dont l'origine n'a pas été explicitement autorisée. C'est une défense en profondeur : elle ne remplace pas le filtrage et l'échappement, elle rattrape leurs défaillances. Cette distinction est importante à énoncer, car une politique bien posée peut donner un faux sentiment de sécurité et faire relâcher l'effort sur la validation des entrées.
La politique couvre également d'autres aspects moins souvent cités : restriction des domaines depuis lesquels des ressources peuvent être chargées, interdiction d'inclure la page dans un cadre sur un autre site, limitation des destinations vers lesquelles des données peuvent être envoyées. Cette dernière directive est particulièrement utile, puisqu'elle empêche l'exfiltration même si du code parvient à s'exécuter. Elle est aussi la plus souvent oubliée dans les politiques que l'on rencontre.
Reste à mesurer ce qu'elle ne fait pas. Elle n'empêche pas une injection de contenu HTML sans script, elle ne protège pas contre une faille côté serveur, et elle ne fait rien contre une extension malveillante installée sur le site. Son périmètre est celui du navigateur et de ce qu'il accepte de charger. Le présenter ainsi évite les attentes déçues et les discussions sur son utilité. Une politique bien posée est une couche parmi d'autres, et elle vaut surtout par ce qu'elle rattrape lorsque le reste a échoué.

Commencer par observer
La première étape ne bloque rien. Elle consiste à déclarer une politique en mode rapport, qui laisse tout passer et signale ce qui aurait été bloqué. Cette phase est indispensable sur un site existant, dont personne ne connaît la liste exhaustive des ressources chargées. Elle l'est d'autant plus que les extensions ajoutent des appels sans jamais les documenter.
L'en tête de rapport se pose à côté de l'en tête normal, ou seul, avec une adresse de collecte vers laquelle le navigateur enverra ses signalements. Chaque violation produit un envoi contenant la directive concernée, la ressource bloquée et la page où cela s'est produit. Ces données constituent l'inventaire que vous n'auriez pas su dresser à la main. Elles proviennent de visiteurs réels, sur des parcours réels, ce qu'aucune exploration automatique ne reproduit fidèlement.
Le point de collecte doit être prévu, faute de quoi les rapports partent dans le vide. Un script d'une vingtaine de lignes, enregistrant les signalements dans un fichier ou une table, suffit largement. Il doit se protéger contre le volume, un site un peu fréquenté produisant rapidement des milliers d'envois pour la même cause. Une déduplication sur la directive et la ressource concernées ramène ce flot à une liste lisible. Un compteur par combinaison suffit à conserver l'information de volume sans stocker chaque envoi.
La durée d'observation doit couvrir un cycle complet d'usage du site, ce qui signifie au moins deux semaines sur un site ordinaire. Les pages rarement visitées, les parcours de paiement et les espaces connectés chargent des ressources que la page d'accueil n'appelle jamais. Écourter cette phase garantit de découvrir un blocage en production, sur le parcours le plus critique. Il vaut mieux consacrer un mois à observer qu'une journée à réparer un tunnel de commande.
La lecture des rapports réserve toujours des surprises : des domaines dont personne ne connaissait l'existence, des scripts hérités d'une ancienne campagne, des ressources chargées par une extension oubliée. Cette découverte constitue à elle seule un bénéfice, indépendamment de la politique elle même. Beaucoup de sites profitent de cette phase pour retirer purement et simplement une partie de ce qu'ils chargeaient. Le gain de performance obtenu au passage est souvent supérieur à celui de plusieurs optimisations menées séparément.
| Étape | Ce qu'elle produit | Durée |
|---|---|---|
| Mode rapport permissif | Inventaire des sources réelles | Deux à quatre semaines |
| Nettoyage des sources inutiles | Politique plus courte | Quelques jours |
| Politique par liste de domaines | Première protection | Une semaine d'observation |
| Suppression du script en ligne | Préparation au nonce | Variable selon le site |
| Passage au nonce | Politique réellement stricte | Une semaine d'observation |
| Retrait des directives héritées | Politique lisible | Quelques minutes |
La liste de domaines et ses limites
La politique la plus répandue autorise le chargement depuis une liste de domaines connus. Elle constitue une étape naturelle après l'observation et elle protège nettement moins qu'on ne le croit.
Sa construction est mécanique : chaque domaine apparu dans les rapports est ajouté à la directive correspondante, scripts, styles, images, connexions, polices, cadres. Le résultat est une politique longue, parfois plusieurs centaines de caractères, qui décrit exactement ce que le site charge aujourd'hui. Elle bloque efficacement tout domaine inconnu, ce qui traite déjà une partie du risque. C'est une amélioration réelle par rapport à l'absence totale de politique, et il faut savoir qu'elle n'est pas la destination.
Sa faiblesse principale tient aux domaines qui hébergent du contenu déposé par des tiers. Autoriser un grand hébergeur de bibliothèques revient à autoriser tout ce qui s'y trouve, y compris des scripts que vous n'avez jamais choisis. Plusieurs contournements documentés reposent exactement sur ce mécanisme, en s'appuyant sur des ressources légitimement présentes chez un fournisseur autorisé. La parade consiste à héberger soi même ces bibliothèques, ce qui présente par ailleurs des avantages de performance.
La seconde faiblesse est l'usure : la liste décrit un état à un instant donné et elle se périme dès qu'un service change de domaine ou qu'une extension en ajoute un. Une politique qui n'est pas maintenue finit par bloquer des ressources légitimes, ce qui pousse les équipes à l'assouplir jusqu'à la vider de son sens. Ce cycle est extrêmement fréquent et il explique la mauvaise réputation du mécanisme. Une politique assouplie deux fois de suite finit généralement par autoriser tout ce qui pose problème.
Enfin, cette approche oblige presque toujours à autoriser le script en ligne, puisque les thèmes et les extensions en produisent en quantité. Cette autorisation annule l'essentiel de la protection contre l'injection, un script injecté étant précisément un script en ligne. C'est la raison pour laquelle la liste de domaines ne peut être qu'une étape et non une destination. Elle a le mérite de faire progresser et elle ne doit pas être présentée comme une protection contre l'injection.
Répartition constatée lors de revues techniques de sites professionnels francophones.
Passer au nonce
Le nonce est une valeur aléatoire, différente à chaque chargement de page, placée à la fois dans la politique et sur chaque balise de script légitime. Le navigateur n'exécute que les scripts portant la bonne valeur, ce qui rend un script injecté inopérant même s'il se trouve dans la page.
La valeur doit être générée à chaque requête par un générateur cryptographique et ne jamais être réutilisée. Un nonce fixe, ou dérivé d'une valeur prévisible, n'apporte strictement aucune protection puisqu'un attaquant peut l'inclure dans son injection. Cette erreur est fréquente sur les mises en place rapides, où la valeur est écrite en dur pour tester puis oubliée. C'est le point à vérifier en priorité sur toute politique employant ce mécanisme. Recharger deux fois la même page et comparer les valeurs suffit à trancher en quelques secondes.
La mise en place suppose que chaque balise de script produite par le site puisse recevoir cet attribut. Sur un site où les scripts sont chargés proprement par les fonctions prévues, un filtre unique ajoute le nonce à toutes les balises produites. Sur un site où des scripts sont écrits en dur dans les gabarits, chacun doit être repris à la main. C'est ce travail préparatoire, et non la politique elle même, qui représente l'essentiel du coût. Il a le mérite d'améliorer la propreté du code au passage, ce qui profite bien au delà de la question de sécurité.
Le nonce se combine avec une directive qui étend la confiance aux scripts chargés par un script déjà autorisé. Sans elle, une bibliothèque qui charge dynamiquement un second fichier serait bloquée, ce qui casse de nombreux outils de mesure et de publicité. Cette directive est ce qui rend le nonce praticable sur un site réel, et elle est mal connue. Son absence explique la plupart des tentatives abandonnées au bout de quelques heures.
La page portant un nonce ne peut évidemment plus être mise en cache telle quelle par un cache partagé, la valeur devant changer à chaque visiteur. Sur un site employant un cache de page, cette contrainte impose soit d'injecter le nonce après le cache, soit d'exclure les pages concernées. C'est le point technique qui bloque le plus souvent le déploiement et il mérite d'être examiné avant de commencer. Certains serveurs et réseaux de diffusion proposent une substitution de la valeur à la volée, ce qui règle élégamment la question.
Une alternative existe pour les scripts servis depuis des fichiers : autoriser leur empreinte plutôt qu'un nonce. Cette approche évite le problème du cache et elle impose de recalculer l'empreinte à chaque modification du fichier. Elle convient bien aux sites statiques et mal aux sites dont le code évolue souvent, ce qui la réserve à des cas précis. Les mécanismes voisins de protection du document sont par ailleurs décrits dans notre article sur les Trusted Types et la protection contre le DOM XSS.
Écrire les directives utiles
La syntaxe est vaste et une politique efficace se construit avec une poignée de directives seulement. En connaître le rôle exact évite d'en écrire dix qui se contredisent.
La directive par défaut sert de repli pour tout ce qui n'est pas déclaré explicitement. La poser à une valeur restrictive, puis n'ouvrir que ce qui est nécessaire, est la bonne approche. Beaucoup de politiques la laissent permissive et déclarent ensuite des restrictions, ce qui produit exactement l'inverse de l'effet recherché.
La directive portant sur les scripts est celle qui compte le plus, puisqu'elle décide de ce qui s'exécute. C'est elle qui reçoit le nonce et c'est sur elle que porte l'essentiel du travail. Une politique dont toutes les autres directives sont parfaites et dont celle ci autorise le script en ligne ne protège pas contre l'injection.
La directive limitant les destinations de connexion mérite une attention particulière, car elle traite le cas où du code parvient malgré tout à s'exécuter. En restreignant les domaines vers lesquels des données peuvent être envoyées, elle empêche l'exfiltration. Elle est simple à poser une fois l'inventaire fait et elle apporte une protection réelle pour un coût nul.
Deux directives complémentaires interdisent l'inclusion de la page dans un cadre sur un autre site et limitent ce que la page peut elle même inclure. La première remplace avantageusement un en tête plus ancien et elle traite une famille d'attaques par superposition. La seconde évite qu'un contenu tiers ne soit affiché dans votre page sans que personne ne l'ait décidé.
Restent les directives sur les styles, les images, les polices et les médias, dont la valeur se déduit directement de l'inventaire. Elles apportent moins de protection que les précédentes et elles complètent utilement l'ensemble. Il vaut mieux les poser explicitement plutôt que de compter sur le repli, la lecture de la politique en étant nettement facilitée.
Déployer sans incident
La progression décrite jusqu'ici évite l'essentiel des problèmes, à condition de respecter quelques précautions au moment de la bascule.
Le passage du mode rapport au mode blocage doit se faire sur une fraction du trafic avant d'être généralisé. Un déploiement sur dix pour cent des visites, pendant quelques jours, révèle les problèmes résiduels avec un impact limité. Cette progressivité demande un peu d'outillage et elle évite d'expliquer à un client pourquoi son tunnel de commande ne fonctionnait plus pendant deux heures.
Le mode rapport doit être conservé après la bascule, en parallèle du mode blocage. Il continue de signaler ce qui est bloqué, information indispensable pour diagnostiquer un incident. Beaucoup de déploiements retirent le rapport en même temps qu'ils activent le blocage, ce qui les prive de toute visibilité au moment précis où elle devient utile.
Les pages sensibles méritent un traitement particulier. Le tunnel de commande, la connexion et les formulaires de paiement doivent être testés manuellement, de bout en bout, avant et après la bascule. Ces parcours chargent souvent des ressources tierces que les rapports n'ont pas captées, faute de visites pendant la période d'observation. Un passage complet prend un quart d'heure et il évite l'incident le plus coûteux.
Une procédure de retour arrière doit exister et être connue. La politique se pose par un en tête, donc son retrait est immédiat, à condition de savoir où il est produit. Sur un site où l'en tête vient d'une extension, d'un fichier de configuration serveur et d'un filtre applicatif, ce retrait devient une chasse au trésor. Une source unique, documentée, règle ce point. Elle doit être décidée dès le début du déploiement, la superposition de plusieurs sources étant ce qui rend les politiques incompréhensibles.
Le suivi après bascule porte sur deux signaux : le volume de violations rapportées, qui doit rester stable, et les signalements des utilisateurs. Une hausse soudaine des violations signale généralement l'ajout d'un service tiers par une équipe qui ignorait la politique. Ce cas se produit régulièrement et il plaide pour informer largement de l'existence de la politique, plutôt que de la traiter comme un réglage technique confidentiel.
Reste à documenter la politique elle même : ce que chaque directive autorise, pourquoi, et qui a demandé chaque exception. Sans cette note, la politique devient un bloc que personne n'ose toucher et qui finit par être désactivé lors du premier incident. Une page suffit, tenue à côté du code qui produit l'en tête. Elle transforme un dispositif fragile en règle maintenable. Elle doit être relue à chaque ajout de service tiers, moment où les exceptions ont tendance à se multiplier sans contrôle. Elle doit également mentionner les services retirés, information tout aussi utile que la liste des services autorisés.
💬 0 commentaires
✍️ Laisser un commentaire
Créez un compte gratuit ou connectez-vous pour commenter.