Quando carichi script, fogli di stile, caratteri o immagini da CDN di terze parti o origini esterne, verifica la loro integrità crittografica con Subresource Integrity (integrity in HTML e integrity() in CSS url()), configura la gestione delle credenziali multiorigine (crossorigin e cross-origin()) e limita le intestazioni Referer in uscita (referrerpolicy e referrer-policy()). Per la Content Security Policy a livello di documento e le liste consentite delle connessioni in uscita, consulta Sicurezza web e Limita le connessioni di rete in uscita con le liste consentite delle connessioni.
1. Genera digest di integrità crittografica per asset immutabili
La funzionalità Subresource Integrity (SRI) garantisce che il browser esegua o esegua il rendering di un asset esterno solo se i suoi byte corrispondono all'hash di crittografia dichiarato dalla tua applicazione.
- Calcola i digest al momento della build o della release, mai da risposte non attendibili in tempo reale: genera gli hash di integrità durante la fase di build o release dai byte degli asset locali noti (o copia il digest ufficiale pubblicato dall'autore del pacchetto) e impostali come hardcoded nel codice HTML, CSS o JavaScript. Non recuperare mai un URL di terze parti nel browser in fase di runtime per calcolare il proprio hash
integrity, che considera attendibili i byte restituiti dal server remoto e annulla l'SRI. - Utilizza
sha384-per impostazione predefinita: formatta i metadati di integrità come<algorithm>-<base64-digest>utilizzandosha384-(impostazione predefinita consigliata),sha512-osha256-. - Fissa le risorse secondarie esterne a URL immutabili con versione: fai sempre riferimento ad asset di terze parti utilizzando URL immutabili, con versione fissa o indirizzati ai contenuti (come
https://cdn.example.com/ui@2.4.0/app.min.js) anziché URL mobili mutabili (latest/app.min.js), che espongono il tuo sito a modifiche silenziose della catena di fornitura e interrompono l'SRI non appena il file remoto viene aggiornato. Se devi caricare uno script di terze parti modificabile che si aggiorna in loco (ad esempio un tag di analisi del fornitore), omettiintegritysu quell'URL e limitane l'accesso con Content Security Policy (strict-dynamic/nonce) o con il sandboxing degli iframe. - Separa più digest con spazi quando un endpoint può pubblicare più payload validi: tutti i browser che supportano SRI implementano
sha256-,sha384-esha512-. Se un URL può legittimamente restituire uno dei diversi payload noti (ad esempio durante un test A/B, un'implementazione CDN in più fasi o una build con negoziazione dei contenuti), elenca più digest dello stesso algoritmo separati da spazi vuoti (ad esempiointegrity="sha384-variantA... sha384-variantB..."). Il browser accetta la risposta se corrisponde a qualsiasi digest elencato dell'algoritmo più efficace presente. - Pubblica risorse secondarie multiorigine con intestazioni CORS: i browser possono verificare l'integrità di una risposta multiorigine solo quando è leggibile da CORS (
Access-Control-Allow-Origin: *per le richieste anonime o l'origine esatta della richiesta conAccess-Control-Allow-Credentials: trueper le richieste con credenziali).
# 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. Script, fogli di stile e precaricamenti HTML sicuri
Imposta un'intestazione della risposta HTTP Referrer-Policy: strict-origin-when-cross-origin a livello di documento (o includi <meta name="referrer" content="strict-origin-when-cross-origin"> in <head>) come valore predefinito di riferimento, in modo che qualsiasi sottorisorsa o CSS url() che omette un modificatore del referrer per richiesta rimuova comunque i parametri di query e percorso sensibili nelle richieste multiorigine.
Poi, sugli elementi esterni <script>, <link rel="stylesheet">, <link rel="preload"> e <link rel="modulepreload">, imposta sempre integrity, crossorigin e referrerpolicy insieme:
integrity="sha384-...": blocca l'esecuzione o l'applicazione della risorsa se i byte della risposta non corrispondono al digest previsto (per un<link rel="stylesheet">bloccato,linkElement.sheetrimanenulle viene attivato un eventoerror).crossorigin="anonymous"(impostazione predefinita consigliata per gli asset CDN pubblici) ocrossorigin="use-credentials": obbligatorio ogni volta cheintegrityè impostato su una risorsa multiorigine. Se ometticrossorigin, il browser recupera la risorsa in modalitàno-cors(generando una risposta opaca che non può essere verificata tramite hash) e blocca la risorsa.referrerpolicy="no-referrer"(impostazione predefinita consigliata per le CDN di terze parti) oreferrerpolicy="strict-origin-when-cross-origin": esegue l'override della policy del referrer del documento per impedire la divulgazione di informazioni sull'origine o sul percorso nell'intestazione della richiestaRefererall'host di terze parti.
<!-- 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>
Quando recuperi risorse secondarie esterne in modo programmatico con fetch(), passa le opzioni integrity, mode, credentials e referrerPolicy corrispondenti (una mancata corrispondenza del digest rifiuta la promessa con 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. Proteggi immagini, caratteri e fogli di stile importati CSS con i modificatori url()
I modificatori di richiesta CSS url() (cross-origin(), referrer-policy() e integrity()) controllano la modalità di recupero, l'intestazione del referrer e la verifica dell'integrità delle risorse secondarie delle risorse caricate tramite CSS, incluse le regole background-image, content, mask-image, border-image-source, filter, @font-face src e @import, direttamente dal CSS:
| Funzione di modifica | Sintassi degli argomenti e valori consentiti | Finalità |
|---|---|---|
cross-origin(<ident>) |
Parola chiave senza virgolette: anonymous (impostazione predefinita consigliata) o use-credentials |
Recupera la risorsa CSS utilizzando CORS e controlla se vengono inviate le credenziali. Includilo sempre insieme a integrity() nelle risorse CSS multiorigine. |
referrer-policy(<ident>) |
Parola chiave senza virgolette: no-referrer (valore predefinito consigliato per gli asset di terze parti), strict-origin-when-cross-origin, origin, same-origin, strict-origin, origin-when-cross-origin, no-referrer-when-downgrade o unsafe-url |
Esegue l'override della policy del referrer del documento per questa specifica richiesta CSS. |
integrity("<string>") |
Stringa tra virgolette: "sha384-<base64>" (o digest separati da spazi "sha384-... sha384-...") |
Verifica la risorsa CSS recuperata rispetto al digest crittografico previsto prima di eseguirne il rendering o di applicarla. |
Segui queste regole di sintassi e sicurezza CSS durante la creazione dei modificatori url():
- Racchiudi la stringa dell'URL tra virgolette all'interno di
url("..."): i modificatori di richiesta sono validi solo quando l'URL all'interno diurl(...)è una<string>tra virgolette (gli URL senza virgolette non possono utilizzare i modificatori). - Specifica ogni tipo di modificatore al massimo una volta per
url(): puoi combinarecross-origin(),referrer-policy()eintegrity()in qualsiasi ordine all'interno diurl("..." ...), ma la ripetizione dello stesso tipo di modificatore due volte (ad esempio due modificatoriintegrity()) non è valida. Per fornire più hash, separali con uno spazio all'interno di una singola stringaintegrity("sha384-... sha384-..."). - Abbina sempre
integrity()multiorigine concross-origin(anonymous): mentre@font-facesrcemask-imagerecuperano già in modalitàcorsanonima per impostazione predefinita,background-image,content,border-image-sourcee@importutilizzano per impostazione predefinita la modalitàno-cors(producendo una risposta opaca che non può essere verificata tramite hash) e l'SVG esternofilterutilizza per impostazione predefinita la modalitàsame-origin(bloccando completamente gli URL multiorigine). L'inclusione dicross-origin(anonymous)(ocross-origin(use-credentials)) garantisce una verifica CORS coerente in ogni proprietà CSS. - Posiziona i modificatori all'interno di
url("...")nelle regole@importe@font-face: mantienicross-origin(),referrer-policy()eintegrity()all'interno diurl("..." ...), mantenendo i descrittori dei caratteri comeformat("woff2")etech(...)all'esterno diurl(...).
@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")
);
}
Strategie di riserva
Se il tuo target Baseline non supporta ancora i modificatori di richiesta CSS url() (cross-origin(), referrer-policy() o integrity()), combina le policy a livello di documento con le dichiarazioni CSS in sequenza in modo che le risorse secondarie rimangano funzionali e protette:
- Preferisci l'HTML
<link rel="stylesheet">al CSS@importquando è richiesto l'SRI cross-browser: poichésubresource-integrityereferrer-policysu<link rel="stylesheet">sono ampiamente disponibili in tutti i browser, carica i fogli di stile esterni con<link rel="stylesheet" href="..." integrity="sha384-..." crossorigin="anonymous" referrerpolicy="no-referrer">anziché@importogni volta che è necessario applicare la verifica dell'integrità in ogni browser. - Affidati all'intestazione
Referrer-Policyo all'elemento<meta name="referrer">a livello di documento del passaggio 2: nei browser che non supportanoreferrer-policy()all'interno del CSSurl(), l'intestazioneReferrer-Policy: strict-origin-when-cross-origino l'elemento<meta name="referrer" content="strict-origin-when-cross-origin">a livello di documento rimuove automaticamente i parametri di query e i percorsi sensibili nelle richieste CSS multiorigine. Utilizza dichiarazioni CSS separate per il potenziamento progressivo: i browser che non riconoscono un modificatore all'interno di
url("..." ...)ignorano l'intera dichiarazione al momento dell'analisi del CSS. Dichiara prima unurl("...")semplice, seguito da una dichiarazionecross-origin()ereferrer-policy()(per i browser che supportano questi due modificatori, ma nonintegrity()), seguita dalla dichiarazione completa, inclusointegrity("sha384-..."):- Non inserire mai un
url()non verificato nello stesso elencosrc@font-faceseparato da virgole: la scrittura disrc: url("font.woff2" cross-origin(anonymous) integrity("sha384-...")), url("font.woff2");in un singolo descrittoresrcbypassa l'integrità delle risorse secondarie in fase di runtime. Se una CDN fornisce un font manomesso che non supera il controllointegrity(), il browser lo considera un errore di caricamento e torna automaticamente al secondourl("font.woff2")separato da virgole. L'utilizzo di dichiarazioni separatesrc:garantisce che i browser supportati sovrascrivano la dichiarazione non verificata al momento dell'analisi del CSS e blocchino i caratteri manomessi in fase di runtime.
@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") ); }- Non inserire mai un
Rileva il supporto del modificatore CSS
url()in JavaScript conCSS.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 };
}