Prévenir l'injection JavaScript n'est pas une seule tâche. C'est fermer plusieurs portes distinctes qui mènent toutes à la même pièce : du code contrôlé par un attaquant s'exécutant dans les navigateurs de vos visiteurs avec tous les privilèges de votre propre page. Certaines de ces portes sont des bugs dans votre code. La plupart sont des scripts que vous avez chargés délibérément et qui sont ensuite devenus hostiles. Ce guide parcourt les contrôles qui réduisent réellement le risque, dans l'ordre qui offre le plus de protection pour le moindre effort, et il est honnête sur l'endroit où chaque contrôle s'arrête.
Si vous voulez d'abord le contexte, ce qu'est l'injection JavaScript couvre le mécanisme et toutes les voies par lesquelles le code étranger entre dans une page. Cet article en est la suite pratique : que faire à ce sujet.
Comment prévenir l'injection JavaScript, en bref
- Fermez la classe XSS dans votre propre code. Encodez à la sortie, utilisez des sinks DOM sûrs, assainissez le HTML non fiable et activez Trusted Types.
- Restreignez la provenance possible du script. Une Content Security Policy stricte basée sur des nonces bloque les origines de script non autorisées.
- Figez ce que vous pouvez. Subresource Integrity fige les dépendances tierces statiques à un hash connu.
- Réduisez l'arbre de dépendances. Chaque script tiers que vous supprimez est une voie d'injection de moins.
- Surveillez l'exécution. Surveillez chaque script qui s'exécute dans des sessions réelles du navigateur, car la prévention ne peut atteindre la compromission d'un fournisseur de confiance.
Les voies d'injection contre lesquelles vous vous défendez
La prévention n'a de sens que lorsque vous savez ce que vous prévenez. Le JavaScript s'injecte par quatre voies principales, et chacune appelle un contrôle différent :
| Voie d'injection | Comment cela se produit | Le contrôle qui aide |
|---|---|---|
| Cross-site scripting (XSS) | Une faille dans votre code permet à l'entrée de l'attaquant de s'exécuter comme du script | Encodage de sortie, sinks sûrs, assainissement, Trusted Types |
| Script tiers compromis | Un fournisseur dont vous chargez le tag distribue du code malveillant | Revue des dépendances, CSP, Subresource Integrity, surveillance à l'exécution |
| Magecart / skimmers | Les attaquants placent du code de vol de cartes sur les pages de paiement, souvent via une dépendance compromise | Surveillance de la charge à l'exécution sur le paiement, contrôles PCI DSS |
| Extensions malveillantes / malware client | Le code est injecté sur la propre machine du visiteur, sur chaque site qu'il visite | Détection à l'exécution ; vous ne pouvez pas corriger le navigateur du visiteur |
La première voie est une faille de code qui vous appartient et que vous pouvez corriger. Les autres sont des failles de confiance : le code a été invité, ou il réside sur une machine que vous ne contrôlez pas. Cette distinction compte, car elle explique pourquoi corriger votre propre code est nécessaire mais jamais suffisant.
Étape 1 : Fermez la classe XSS dans votre propre code
Le cross-site scripting est la seule voie d'injection entièrement sous votre contrôle, alors commencez par là. Le XSS se produit lorsqu'une donnée fournie par un utilisateur est écrite dans la page à un endroit où le navigateur la traitera comme du code plutôt que comme du texte. Les correctifs sont bien compris :
- Encodez à la sortie, pas seulement à l'entrée. La même chaîne est sûre dans un contexte et dangereuse dans un autre, alors encodez les données pour le contexte exact où elles atterrissent : corps HTML, attribut HTML, JavaScript, URL ou CSS. L'encodage de sortie sensible au contexte est le contrôle anti-XSS le plus efficace.
- Utilisez des sinks DOM sûrs. Affectez les données non fiables avec
textContentplutôt qu'avecinnerHTML, et évitezeval,document.writeetsetAttributesur les gestionnaires d'événements. Ces « sinks » sont l'endroit où les chaînes injectées deviennent exécutables. - Assainissez le HTML que vous devez rendre. Lorsque vous devez réellement rendre du HTML fourni par l'utilisateur (un commentaire en texte enrichi, par exemple), passez-le par un assainisseur maintenu qui retire le script et les gestionnaires d'événements, plutôt que d'écrire votre propre filtre.
- Activez Trusted Types. Trusted Types font en sorte que les sinks DOM dangereux refusent les chaînes simples au niveau du navigateur, de sorte qu'une charge injectée ne peut les atteindre même si un bug passe la revue de code. Ils sont appliqués via une directive CSP, ce qui mène directement à l'étape suivante.
Corriger la classe XSS supprime la porte qu'un attaquant ouvre en exploitant votre code. Cela ne fait rien contre les portes que vous avez ouvertes vous-même en chargeant le script de quelqu'un d'autre, d'où provient la plupart des brèches modernes côté client.
Étape 2 : Restreignez les origines avec une Content Security Policy
Une Content Security Policy indique au navigateur quelles origines sont autorisées à charger et exécuter du script sur votre page. Une CSP stricte basée sur des nonces est l'un des contrôles individuels les plus puissants que vous puissiez déployer contre l'injection : un script qui ne provient pas d'une origine autorisée, et qui ne porte pas le nonce correct par requête, ne s'exécute tout simplement pas. Les blocs <script> en ligne qu'un attaquant injecte via une faille XSS sont bloqués par défaut.
Deux remarques pratiques. D'abord, préférez les nonces aux listes d'hôtes autorisés lorsque vous le pouvez ; une large liste script-src rouvre discrètement des voies que vous vouliez fermer. Ensuite, et c'est la limite importante : la CSP autorise des origines, elle ne juge pas le comportement. Une fois que vous autorisez le domaine d'un fournisseur pour que son script légitime s'exécute, la CSP n'a aucun moyen de savoir si ce script se comporte bien ou écrème votre formulaire. Si le fournisseur est compromis à la source, son domaine reste sur votre liste d'autorisation et la version malveillante se charge sans encombre. La CSP est nécessaire. Elle n'est pas la réponse complète.
Étape 3 : Figez les dépendances statiques avec Subresource Integrity
Subresource Integrity (SRI) vous permet d'attacher un hash cryptographique à un tag <script> ou <link>. Le navigateur calcule le hash du fichier qu'il a téléchargé et refuse de l'exécuter si le hash ne correspond pas, ce qui signifie qu'un fichier tiers statique ne peut être remplacé par une version altérée sans que le changement soit bloqué.
SRI est excellent pour les dépendances qui ne changent pas : une version spécifique d'une bibliothèque figée à son hash exact. Sa limite est le miroir de cette force. Il ne fait rien pour les scripts destinés à se mettre à jour (la plupart des analyses, gestionnaires de tags et SDK de paiement changent régulièrement le fichier servi), et il ne fait rien pour les scripts chargés dynamiquement par d'autres scripts. Utilisez SRI partout où vous pouvez figer une version. N'attendez pas qu'il couvre les parties de votre pile qui se mettent à jour toutes seules.
Étape 4 : Réduisez et revoyez l'arbre de dépendances
Chaque script tiers sur votre page est une voie d'injection dotée de tous les privilèges de la page. La prévention la moins coûteuse disponible est d'en avoir moins.
- Inventoriez ce que vous chargez réellement. La plupart des équipes sont surprises du nombre de scripts qui s'exécutent sur une page typique, et du nombre de ceux qui sont chargés non pas directement mais par un autre script (un gestionnaire de tags chargeant un fournisseur chargeant un autre fournisseur).
- Supprimez ce dont vous n'avez pas besoin. Un pixel marketing inutilisé ou un outil de test A/B abandonné est un pur risque sans bénéfice. Le supprimer élimine purement et simplement une voie d'injection.
- Minimisez le rayon d'impact du gestionnaire de tags. Un compte de gestionnaire de tags compromis peut injecter « juste un tag de plus » qui semble anodin. Restreignez qui peut publier et revoyez ce qu'ils publient.
Cette étape n'a aucun inconvénient et aucun contrôle par script ne peut la remplacer : un script qui n'est pas sur la page ne peut être celui qui est compromis.
Étape 5 : Surveillez l'exécution avec l'inspection de la charge en sessions réelles
Les étapes une à quatre réduisent la probabilité. Aucune ne peut atteindre le cas qui cause les pires brèches côté client : un script auquel vous faites légitimement confiance, d'une origine que vous avez légitimement autorisée, qui devient malveillant après avoir déjà été chargé. La CSP le laisse passer parce que l'origine est autorisée. SRI le manque parce que le script est conçu pour se mettre à jour. Votre propre code est propre. Le seul endroit restant pour l'attraper est là où l'injection atterrit réellement, le navigateur, au moment où le script s'exécute.
C'est ce que fait la surveillance à l'exécution. Elle construit un inventaire de chaque script qui s'exécute dans des sessions réelles d'utilisateurs, enregistre ce que fait chacun et où il envoie des données, et alerte lorsqu'un nouveau script apparaît, que le code d'un script connu change, ou que des données commencent à circuler vers une destination inattendue. C'est la couche qui attrape les skimmers de type Magecart et les attaques de fournisseurs compromis que tous les contrôles préventifs ci-dessus laissent passer.
Les outils côté serveur ne peuvent pas fournir cela. Un pare-feu applicatif web ou une analyse serveur voit le HTML que vous avez livré, pas la charge qu'un fournisseur compromis remplace ensuite ni le code qu'une extension injecte sur la machine du visiteur. La détection doit se produire au niveau du navigateur, dans des sessions réelles.
Où cside s'intègre : surveillance de scripts en sessions réelles
cside est un unique script JavaScript propriétaire (first-party) qui fournit la couche d'exécution de cette liste de contrôle. Il ne remplace pas votre CSP, votre encodage de sortie ni vos hashs SRI ; c'est le contrôle qui surveille ce que vos scripts autorisés font réellement une fois qu'ils s'exécutent, ce qu'aucun des autres ne peut voir.
La surveillance de scripts de cside inventorie chaque script qui s'exécute dans des sessions réelles du navigateur, analyse la charge complète du script et alerte sur les nouveaux domaines, le code modifié et les flux de données inattendus. Elle fonctionne selon deux modèles opératoires : le Script Method récupère et analyse les scripts tiers du côté de cside avant qu'ils ne s'exécutent dans la session, et le Scan Method pour les équipes qui préfèrent une empreinte plus légère. Elle se déploie comme un seul tag de script depuis votre propre origine, sans changement de DNS ; cside récupère et analyse les scripts tiers que charge votre page, mais ne se place pas devant le trafic de votre site et n'agit pas comme un proxy.
Sur les pages de paiement, cet inventaire à l'exécution est aussi une exigence de conformité. Les exigences 6.4.3 et 11.6.1 de PCI DSS 4.0.1 existent précisément parce que le script injecté sur la page de paiement est invisible pour tous les contrôles côté serveur : 6.4.3 vous demande de gérer et d'autoriser chaque script sur vos pages de paiement, et 11.6.1 vous demande de détecter les modifications non autorisées de ces scripts et des en-têtes de page. La surveillance de scripts en sessions réelles est la réponse directe aux deux.
Liste de contrôle de prévention
La prévention de l'injection JavaScript est en couches, et les couches ne se recoupent pas :
- Encodez la sortie et utilisez des sinks DOM sûrs pour fermer la classe XSS dans votre propre code.
- Déployez une CSP stricte basée sur des nonces pour bloquer les origines de script non autorisées.
- Ajoutez Subresource Integrity pour figer les dépendances tierces statiques.
- Réduisez et revoyez l'arbre de dépendances pour qu'il y ait moins à compromettre.
- Surveillez les sessions réelles du navigateur afin qu'un script de confiance devenu hostile soit attrapé au moment où il agit.
Réussissez les quatre premières et vous aurez fermé la plupart des portes. Ajoutez la cinquième et vous pourrez voir la seule porte que la prévention ne peut jamais verrouiller entièrement : le fournisseur de confiance qui est compromis après que vous l'avez laissé entrer.









