Lorsque vous chargez des scripts, des feuilles de style, des polices ou des images à partir de CDN tiers ou d'origines externes, vérifiez leur intégrité cryptographique avec l'intégrité des sous-ressources (integrity en HTML et integrity() en CSS url()), configurez la gestion des identifiants interorigines (crossorigin et cross-origin()) et limitez les en-têtes Referer sortants (referrerpolicy et referrer-policy()). Pour en savoir plus sur la Content Security Policy au niveau du document et les listes d'autorisation de connexions sortantes, consultez Sécurité Web et Limiter les connexions réseau sortantes avec des listes d'autorisation de connexions.
1. Générer des condensés d'intégrité cryptographique pour les assets immuables
L'intégrité des sous-ressources (SRI, Subresource Integrity) garantit que le navigateur n'exécute ou n'affiche un élément externe que si ses octets correspondent au hachage cryptographique déclaré par votre application.
- Calculez les condensés au moment de la compilation ou de la publication, jamais à partir de réponses non fiables en direct : générez des hachages d'intégrité lors de l'étape de compilation ou de publication à partir d'octets d'éléments locaux connus (ou copiez le condensé officiel publié par l'auteur du package) et codez-les en dur dans votre code HTML, CSS ou JavaScript. Ne récupérez jamais une URL tierce dans le navigateur au moment de l'exécution pour calculer son propre hachage
integrity, qui fait confiance aux octets renvoyés par le serveur distant et annule l'effet de la SRI. - Utiliser
sha384-par défaut : formatez les métadonnées d'intégrité en tant que<algorithm>-<base64-digest>à l'aide desha384-(valeur par défaut recommandée),sha512-ousha256-. - Épinglez les sous-ressources externes à des URL immuables et versionnées : référencez toujours les éléments tiers à l'aide d'URL immuables, versionnées ou adressées au contenu (comme
https://cdn.example.com/ui@2.4.0/app.min.js) plutôt que d'URL flottantes modifiables (latest/app.min.js), qui exposent votre site à des modifications silencieuses de la chaîne d'approvisionnement et interrompent le SRI dès que le fichier distant est mis à jour. Si vous devez charger un script tiers modifiable qui se met à jour sur place (comme un tag d'analyse de fournisseur), omettezintegritysur cette URL et limitez-le plutôt avec Content Security Policy (strict-dynamic/nonces) ou le sandboxing d'iframe. - Séparez plusieurs condensés par un espace lorsqu'un point de terminaison peut diffuser plusieurs charges utiles valides : tous les navigateurs compatibles avec le SRI implémentent
sha256-,sha384-etsha512-. Si une URL peut légitimement renvoyer l'une des charges utiles connues (par exemple, lors d'un test A/B, d'un déploiement CDN par étapes ou d'un build négocié par le contenu), listez plusieurs condensés du même algorithme séparés par un espace (par exemple,integrity="sha384-variantA... sha384-variantB..."). Le navigateur accepte la réponse si elle correspond à n'importe quel condensé listé de l'algorithme le plus puissant présent. - Diffusez des sous-ressources interorigines avec des en-têtes CORS : les navigateurs ne peuvent vérifier l'intégrité d'une réponse interorigine que lorsqu'elle est lisible par CORS (
Access-Control-Allow-Origin: *pour les requêtes anonymes ou l'origine exacte de la requête avecAccess-Control-Allow-Credentials: truepour les requêtes authentifiées).
# Generate a sha384- SRI hash at build time from a known-good local file
printf "sha384-%s\n" "$(openssl dgst -sha384 -binary app.min.js | openssl base64 -A)"
// Build-time / tooling helper (Node.js 19+, Deno, Bun) to compute an SRI string from known-good local bytes
export async function computeIntegrityMetadata(bytes) {
const rawBytes =
typeof bytes === 'string' ? new TextEncoder().encode(bytes) : bytes;
const digestBuffer = await crypto.subtle.digest('SHA-384', rawBytes);
const binary = Array.from(new Uint8Array(digestBuffer), (b) =>
String.fromCharCode(b),
).join('');
return `sha384-${btoa(binary)}`;
}
2. Scripts HTML, feuilles de style et préchargements sécurisés
Définissez un en-tête de réponse HTTP Referrer-Policy: strict-origin-when-cross-origin au niveau du document (ou incluez <meta name="referrer" content="strict-origin-when-cross-origin"> dans <head>) comme valeur par défaut de référence. Ainsi, toute sous-ressource ou url() CSS qui omet un modificateur de provenance par requête supprime toujours les paramètres de chemin d'accès et de requête sensibles des requêtes interorigines.
Ensuite, sur les éléments externes <script>, <link rel="stylesheet">, <link rel="preload"> et <link rel="modulepreload">, définissez toujours integrity, crossorigin et referrerpolicy ensemble :
integrity="sha384-...": bloque l'exécution ou l'application de la ressource si les octets de réponse ne correspondent pas au condensé attendu (pour un<link rel="stylesheet">bloqué,linkElement.sheetrestenullet un événementerrorse déclenche).crossorigin="anonymous"(valeur par défaut recommandée pour les éléments CDN publics) oucrossorigin="use-credentials": obligatoire chaque fois queintegrityest défini sur une ressource interorigine. Si vous omettezcrossorigin, le navigateur récupère la ressource en modeno-cors(ce qui produit une réponse opaque qui ne peut pas être validée par hachage) et bloque la ressource.referrerpolicy="no-referrer"(valeur par défaut recommandée pour les CDN tiers) oureferrerpolicy="strict-origin-when-cross-origin": remplace la règle de provenance du document pour empêcher la fuite d'informations sur l'origine ou le chemin d'accès dans l'en-tête de requêteReferervers l'hôte tiers.
<!-- Document-level baseline referrer policy in <head> (or via Referrer-Policy HTTP header) -->
<meta name="referrer" content="strict-origin-when-cross-origin" />
<!-- External stylesheet with SRI, anonymous CORS, and no referrer -->
<link
rel="stylesheet"
href="https://cdn.example.com/ui@2.4.0/styles.css"
integrity="sha384-bs0EksOYBbU/g0IAHMXbSyH1Mgw83GKn+EZlSI+VE+zVchXmSbLwhVLrPzsOWnc5"
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<!-- External script with SRI, anonymous CORS, and no referrer -->
<script
src="https://cdn.example.com/ui@2.4.0/bundle.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
Lorsque vous récupérez des sous-ressources externes de manière programmatique avec fetch(), transmettez les options integrity, mode, credentials et referrerPolicy correspondantes (une non-concordance de condensé rejette la promesse avec TypeError) :
const response = await fetch('https://cdn.example.com/ui@2.4.0/manifest.json', {
integrity: 'sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC',
mode: 'cors',
credentials: 'omit',
referrerPolicy: 'no-referrer',
});
3. Sécurisez les images, les polices et les feuilles de style importées CSS avec les modificateurs url()
Les modificateurs de requête CSS url() (cross-origin(), referrer-policy() et integrity()) contrôlent le mode de récupération, l'en-tête de provenance et la validation de l'intégrité des sous-ressources des ressources chargées par CSS, y compris les règles background-image, content, mask-image, border-image-source, filter, @font-face src et @import, directement à partir de CSS :
| Fonction de modificateur | Syntaxe des arguments et valeurs autorisées | Objectif |
|---|---|---|
cross-origin(<ident>) |
Mot clé sans guillemets : anonymous (valeur par défaut recommandée) ou use-credentials |
Récupère la ressource CSS à l'aide de CORS et contrôle si les identifiants sont envoyés. Incluez toujours cet attribut avec integrity() sur les ressources CSS interorigines. |
referrer-policy(<ident>) |
Mot clé sans guillemets : no-referrer (valeur par défaut recommandée pour les assets tiers), strict-origin-when-cross-origin, origin, same-origin, strict-origin, origin-when-cross-origin, no-referrer-when-downgrade ou unsafe-url |
Remplace la règle de provenance du document pour cette requête CSS spécifique. |
integrity("<string>") |
Chaîne entre guillemets : "sha384-<base64>" (ou condensés séparés par des espaces "sha384-... sha384-...") |
Vérifie la ressource CSS récupérée par rapport au condensé cryptographique attendu avant de l'afficher ou de l'appliquer. |
Suivez ces règles de syntaxe et de sécurité CSS lorsque vous créez des modificateurs url() :
- Mettez la chaîne d'URL entre guillemets dans
url("..."): les modificateurs de requête ne sont valides que lorsque l'URL dansurl(...)est une<string>entre guillemets (les URL sans guillemets ne peuvent pas utiliser de modificateurs). - Spécifiez chaque type de modificateur au maximum une fois par
url(): vous pouvez combinercross-origin(),referrer-policy()etintegrity()dans n'importe quel ordre dansurl("..." ...), mais répéter deux fois le même type de modificateur (par exemple, deux modificateursintegrity()) n'est pas valide. Pour fournir plusieurs hachages, séparez-les par un espace dans une seule chaîneintegrity("sha384-... sha384-..."). - Associez toujours
integrity()interorigine àcross-origin(anonymous): alors que@font-facesrcetmask-imagerécupèrent déjà en modecorsanonyme par défaut,background-image,content,border-image-sourceet@importsont définis par défaut sur le modeno-cors(produisant une réponse opaque qui ne peut pas être vérifiée par hachage) et lefilterSVG externe est défini par défaut sur le modesame-origin(bloquant complètement les URL interorigines). L'inclusion decross-origin(anonymous)(oucross-origin(use-credentials)) garantit une vérification CORS cohérente pour chaque propriété CSS. - Placer les modificateurs dans
url("...")dans les règles@importet@font-face: conservezcross-origin(),referrer-policy()etintegrity()dansurl("..." ...), tout en conservant les descripteurs de police tels queformat("woff2")ettech(...)en dehors deurl(...).
@import url(
"https://cdn.example.com/theme@1.2.0/tokens.css"
cross-origin(anonymous)
referrer-policy(no-referrer)
integrity("sha384-bs0EksOYBbU/g0IAHMXbSyH1Mgw83GKn+EZlSI+VE+zVchXmSbLwhVLrPzsOWnc5")
);
@font-face {
font-family: "BrandSans";
src: url(
"https://cdn.example.com/fonts@1.0.0/brand-sans.woff2"
cross-origin(anonymous)
referrer-policy(no-referrer)
integrity("sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC")
) format("woff2");
font-display: swap;
}
.verified-graphic {
background-image: url(
"https://cdn.example.com/media@1.0.0/badge.svg"
cross-origin(anonymous)
referrer-policy(no-referrer)
integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91")
);
}
Stratégies de remplacement
Si votre cible Baseline n'est pas encore compatible avec les modificateurs de requête CSS url() (cross-origin(), referrer-policy() ou integrity()), combinez des règles au niveau du document avec des déclarations CSS en cascade pour que les sous-ressources restent fonctionnelles et protégées :
- Privilégiez le code HTML
<link rel="stylesheet">au code CSS@importlorsque l'intégrité des sous-ressources (SRI) est requise sur plusieurs navigateurs : étant donné quesubresource-integrityetreferrer-policysur<link rel="stylesheet">sont largement disponibles sur tous les navigateurs, chargez les feuilles de style externes avec<link rel="stylesheet" href="..." integrity="sha384-..." crossorigin="anonymous" referrerpolicy="no-referrer">au lieu de@importchaque fois que la validation de l'intégrité doit être appliquée dans chaque navigateur. - Utilisez l'en-tête
Referrer-Policyou l'élément<meta name="referrer">au niveau du document de l'étape 2 : dans les navigateurs qui n'acceptent pasreferrer-policy()dans le CSSurl(), l'en-têteReferrer-Policy: strict-origin-when-cross-originou l'élément<meta name="referrer" content="strict-origin-when-cross-origin">au niveau du document supprime automatiquement les paramètres de chemin d'accès et les paramètres de requête sensibles des requêtes CSS interorigines. Utilisez des déclarations CSS distinctes en cascade pour l'amélioration progressive : les navigateurs qui ne reconnaissent pas un modificateur dans
url("..." ...)ignorent l'intégralité de la déclaration au moment de l'analyse CSS. Déclarez d'abord unurl("...")simple, suivi d'une déclarationcross-origin()etreferrer-policy()(pour les navigateurs qui acceptent ces deux modificateurs, mais pasintegrity()), puis de la déclaration complète incluantintegrity("sha384-..."):- Ne placez jamais un
url()non validé dans la même liste@font-facesrcséparée par une virgule : l'écriture desrc: url("font.woff2" cross-origin(anonymous) integrity("sha384-...")), url("font.woff2");dans un seul descripteursrccontourne l'intégrité des sous-ressources au moment de l'exécution. Si un CDN diffuse une police falsifiée qui échoue à la vérificationintegrity(), le navigateur la traite comme un échec de chargement et revient automatiquement au deuxièmeurl("font.woff2")séparé par une virgule. L'utilisation de déclarations distinctessrc:garantit que les navigateurs compatibles écrasent la déclaration non validée au moment de l'analyse CSS et bloquent les polices falsifiées au moment de l'exécution.
@font-face { font-family: "BrandSans"; /* 1. Parse-time fallback for browsers without url() modifier support */ src: url("https://cdn.example.com/fonts@1.0.0/brand-sans.woff2") format("woff2"); /* 2. Parse-time fallback for browsers supporting cross-origin() and referrer-policy() */ src: url( "https://cdn.example.com/fonts@1.0.0/brand-sans.woff2" cross-origin(anonymous) referrer-policy(no-referrer) ) format("woff2"); /* 3. Full protection in browsers supporting cross-origin(), referrer-policy(), and integrity() */ src: url( "https://cdn.example.com/fonts@1.0.0/brand-sans.woff2" cross-origin(anonymous) referrer-policy(no-referrer) integrity("sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC") ) format("woff2"); font-display: swap; } .verified-graphic { background-image: url("https://cdn.example.com/media@1.0.0/badge.svg"); background-image: url( "https://cdn.example.com/media@1.0.0/badge.svg" cross-origin(anonymous) referrer-policy(no-referrer) ); background-image: url( "https://cdn.example.com/media@1.0.0/badge.svg" cross-origin(anonymous) referrer-policy(no-referrer) integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91") ); }- Ne placez jamais un
Détectez la compatibilité du modificateur CSS
url()dans JavaScript avecCSS.supports():
export function getCssUrlModifierSupport() {
const supportsCrossOriginAndReferrer =
typeof CSS !== 'undefined' &&
typeof CSS.supports === 'function' &&
CSS.supports(
'background-image',
'url("https://example.com/a.svg" cross-origin(anonymous) referrer-policy(no-referrer))',
);
const supportsUrlIntegrity =
typeof CSS !== 'undefined' &&
typeof CSS.supports === 'function' &&
CSS.supports(
'background-image',
'url("https://example.com/a.svg" cross-origin(anonymous) referrer-policy(no-referrer) integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91"))',
);
return { supportsCrossOriginAndReferrer, supportsUrlIntegrity };
}