Comment protéger les scripts CDN avec Subresource Integrity
CentralCSP Team ·
Dernière mise à jour:
Quand vous chargez un script depuis un CDN, vous faites confiance à quiconque contrôle ce serveur pour ne jamais servir autre chose que le fichier attendu. Subresource Integrity (SRI) supprime cette confiance. Vous ajoutez un hash cryptographique du fichier attendu à la balise <script> ou <link>, et le navigateur refuse d'exécuter tout ce qui ne correspond pas.
En résumé : mettez un hash integrity sur chaque script et feuille de style tiers, et associez-le à crossorigin pour que le navigateur puisse le vérifier. Si un CDN est compromis et se met à servir du code altéré, le navigateur bloque le chargement au lieu d'exécuter le fichier trafiqué. Cet article couvre la syntaxe exacte, comment le navigateur choisit quel hash vérifier, pourquoi crossorigin est obligatoire pour les fichiers cross-origin, et comment le SRI s'articule avec votre politique de sécurité du contenu.
Qu'est-ce que Subresource Integrity ?
Le SRI est une spécification W3C qui ajoute un attribut integrity aux éléments HTML qui chargent des ressources externes. La valeur est un hash du contenu attendu. Quand le navigateur récupère la ressource, il hashe les octets reçus et les compare à votre valeur. Une correspondance exécute le fichier. Une non-correspondance est traitée comme une erreur réseau, la ressource n'est donc ni chargée, ni appliquée, ni exécutée.
Ce seul contrôle couvre un risque réel. Un compte CDN est compromis, un pipeline de build est empoisonné, ou un homme du milieu échange la réponse, et votre page se met discrètement à exécuter du code d'attaquant. Avec le SRI en place, toute modification d'un octet du fichier casse le hash et le navigateur l'écarte.
L'attribut integrity est pris en charge sur <script>, et sur <link> avec un rel de stylesheet, preload ou modulepreload.
Comment utiliser l'attribut integrity
La valeur est un ou plusieurs hashes. Chaque hash est un préfixe d'algorithme (sha256-, sha384- ou sha512-), un tiret, puis le digest encodé en base64. Plusieurs hashes sont séparés par des espaces. Le SRI impose aux navigateurs de prendre en charge SHA-256, SHA-384 et SHA-512 ; SHA-384 est un choix par défaut courant.
Voici un script cross-origin avec un hash integrity et l'attribut crossorigin dont il a besoin :
<script
src="https://cdn.example.com/script.js"
integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7"
crossorigin="anonymous"></script>Une feuille de style fonctionne de la même manière :
<link
rel="stylesheet"
href="https://cdn.example.com/styles.css"
integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7"
crossorigin="anonymous">Vous générez le hash à partir du fichier exact que sert le CDN. Collez l'URL ou le fichier dans le générateur SRI, qui produit pour vous les valeurs SHA-256, SHA-384 et SHA-512. Le SRI ne se met pas à jour automatiquement : si le CDN livre légitimement une nouvelle version du fichier, vous régénérez le hash, ou la ressource cesse de se charger. C'est la fonctionnalité qui marche comme prévu, pas un bug.
Quel hash le navigateur vérifie-t-il ?
Vous pouvez lister plus d'un hash, et c'est là que vit une idée reçue courante. Le navigateur n'utilise pas le premier hash, et il n'utilise pas le premier algorithme qu'il prend en charge. Il utilise l'algorithme le plus fort présent.
Si votre valeur integrity contient à la fois un hash SHA-256 et un hash SHA-384, le navigateur sélectionne les hashes SHA-384 et ignore entièrement ceux en SHA-256. Lister un hash plus faible à côté d'un plus fort n'affaiblit pas le contrôle.
<script
src="https://cdn.example.com/script.js"
integrity="sha256-yourSha256Digest
sha384-yourSha384Digest"
crossorigin="anonymous"></script>Dans cet exemple, seul le hash SHA-384 est utilisé. Ce comportement est défini dans la spécification SRI (« get the strongest metadata from set ») et correspond à la façon dont les navigateurs l'implémentent : le navigateur valide contre l'algorithme le plus fort que vous listez, donc SHA-512 l'emporte sur SHA-384, qui l'emporte sur SHA-256.
Lister plusieurs hashes du même algorithme se comporte différemment et est utile. Le navigateur les garde tous, et la ressource valide si l'un d'eux correspond. Cela vous permet d'accepter plus d'une version connue-bonne d'un fichier à la même URL.
Pourquoi crossorigin est requis
Pour une ressource cross-origin, l'attribut integrity ne fait rien à lui seul. Vous avez aussi besoin de crossorigin, et l'omettre est la raison unique la plus fréquente pour laquelle le SRI échoue silencieusement.
La raison est CORS. Sans crossorigin, le navigateur récupère un fichier cross-origin en mode no-cors, et la réponse est opaque : votre page ne peut pas lire ses octets. Les navigateurs refusent d'exécuter le SRI sur une requête no-cors, donc le chargement échoue. Autoriser le contrôle sur des réponses opaques divulguerait aussi des données, un attaquant pourrait forcer par force brute du contenu cross-origin en testant des hashes contre lui, donc CORS force le propriétaire de la ressource à partager explicitement le fichier avant que le SRI puisse le lire.
L'attribut crossorigin a deux valeurs utiles :
anonymous(ou une valeur vide) utilise CORS sans envoyer de cookies ni d'identifiants en cross-origin. Utilisez-la pour les fichiers de CDN publics.use-credentialsutilise CORS et envoie toujours les identifiants. Utilisez-la seulement quand le tiers l'exige.
Les ressources same-origin sont différentes. Elles sont déjà dans un état où le partage est permis, donc un <script> same-origin avec integrity est vérifié sans avoir besoin de crossorigin. L'attribut est l'exigence du cross-origin.
SRI et politique de sécurité du contenu
Le SRI vérifie le contenu d'un fichier que vous avez déjà décidé de charger. Une politique de sécurité du contenu (CSP) décide quels fichiers ont le droit de se charger tout court. Ils résolvent deux moitiés différentes du même problème, et fonctionnent bien ensemble.
Une directive CSP script-src contrôle les origines et le code inline que votre page peut exécuter. Le SRI garantit ensuite que les fichiers tiers spécifiques que vous autorisez n'ont pas été altérés. Si vous construisez une politique de zéro, comment construire une CSP solide et la référence script-src couvrent les directives, et la page hashes et nonce CSP explique comment la CSP utilise ses propres hashes (un mécanisme distinct du SRI, pour autoriser du code inline ; voyez SRI vs hash CSP pour la distinction), et comment générer un hash CSP détaille comment en produire un.
Il exista jadis une directive CSP, require-sri-for, censée forcer le SRI sur chaque script ou style. N'y touchez pas : elle a été retirée de la spec et n'a jamais atterri dans les navigateurs stables. Elle n'est pas interopérable aujourd'hui. La direction moderne pour exiger l'intégrité à l'échelle du site est le header Integrity-Policy, qui peut bloquer les scripts dépourvus d'intégrité et les signaler comme une integrity-violation. Il est récemment devenu disponible dans les versions actuelles de Chrome, Firefox et Safari ; le guide appliquer le SRI partout avec Integrity-Policy le détaille. Pour la référence complète de l'attribut, du hashing et d'Integrity-Policy, voyez Subresource Integrity (SRI).
Une checklist rapide
- Ajoutez
integrityà chaque<script>et<link rel="stylesheet">cross-origin que vous contrôlez. - Associez toujours un
integritycross-origin àcrossorigin="anonymous". - Préférez SHA-384 ou SHA-512 ; si vous listez plusieurs algorithmes, le navigateur utilise le plus fort.
- Régénérez le hash chaque fois que le fichier amont change, ou automatisez les hashes SRI dans votre build.
Le SRI durcit le code tiers que vous chargez déjà, et un inventaire de scripts vous dit ce qu'est réellement ce code tiers. CentralCSP construit un inventaire de chaque script qui tourne sur vos pages à partir des rapports de hash CSP, pour que vous voyiez les scripts nouveaux ou modifiés avant qu'ils ne deviennent un incident. Commencez gratuitement pour cartographier vos dépendances côté client.