Perché il file che il tuo sito carica da fuori può cambiare
Il file che il tuo sito carica da un altro indirizzo può arrivare diverso da quello scelto. Il canale sicuro non fissa i byte.
In questa guida (8)
Il file che il tuo sito carica da un altro indirizzo può arrivare diverso da quello che hai scelto. Il canale sicuro dice con quale server parla il browser, non se i byte sono quelli. L'attributo che fissa i byte si chiama integrity. Il meccanismo si chiama Subresource Integrity. Il 2 novembre 2026 ho letto la bozza degli editori del 20 marzo 2026 e la pagina MDN, ultima modifica 14 aprile 2026. Non ho misurato un browser e non ho calcolato un'impronta. La Raccomandazione su w3.org non l'ho aperta.
Questa pagina non descrive il banner dei cookie. Chi può leggere la risposta di un altro sito sta nell'articolo un altro sito può leggere la risposta del tuo sito. Non lo riscrivo. Content-Security-Policy non l'ho aperto.

Il confronto usa solo l'algoritmo più forte
La sezione 3.1 chiama integrity metadata tre pezzi: la funzione di hash, il digest, e le opzioni. Funzione e digest sono obbligatori. Di opzioni, oggi, non ne è definita nessuna. Il digest è il risultato della funzione, codificato in base64 come nella sezione 4 di RFC 4648. I nomi ammessi, sezione 2, sono sha256, sha384 e sha512, in quest'ordine di forza: il primo è il più debole, l'ultimo il più forte. Un nome è valido se, messo in minuscolo ASCII, sta in quell'insieme. Il confronto del digest, sezione 3.3.4, è invece sensibile alle maiuscole.
Il parse, sezione 3.3.2, spezza la stringa sugli spazi. Per ogni pezzo prende il nome prima del primo trattino e il valore nel pezzo successivo. Un secondo trattino non viene riattaccato: il passo legge solo quel pezzo. Se il nome non è valido, lo salta. Le opzioni dopo un punto interrogativo non vengono nemmeno conservate. La sezione 3.5 dice che un'opzione non riconosciuta va ignorata, e che la grammatica ammette anche la stringa vuota.
Poi la sezione 3.3.3 tiene solo il gruppo più forte. Se nella stessa stringa ci sono sha256 e sha384, resta la seconda. Le sha256 non sono una riserva: se la forte non coincide, il confronto fallisce lo stesso. Se di quella forza ce n'è più d'una, la sezione 3.3.4 restituisce true alla prima che coincide. Se non ne coincide nessuna, restituisce false. MDN descrive gli stessi due casi.
Se il parse non lascia nessun nome capito, la sezione 3.3.4 restituisce true. Il file non viene scartato da questo algoritmo. La sezione 3.2.1 lo dice per l'agilità: un browser vecchio, che non conosce la funzione nuova, si comporta come se quell'impronta non ci fosse, e può ancora usare una più debole che conosce. Il rovescio è che, il giorno in cui un browser toglie una funzione, una pagina che ha solo quella non blocca più il file. MDN, nel paragrafo su come il browser sceglie, non descrive questo caso del risultato vuoto. Non ho corretto una pagina con l'altra.
Dove si scrive l'impronta
La sezione 3.4 aggiunge l'attributo agli elementi link e script. Una revisione futura, dice la stessa sezione, potrebbe coprire anche altri elementi, e nomina il collegamento, l'audio, il frame, l'immagine, l'oggetto, la sorgente, la traccia e il video. Oggi quell'elenco non c'è. Una foto non entra in questo controllo. MDN restringe il link a tre relazioni: stylesheet, preload e modulepreload. La bozza, nella sezione 3.4, non elenca quelle relazioni. Non ho aperto il modello di HTML, quindi non decido io quali relazioni fanno partire il confronto.
La sezione 3.6 usa la stessa grammatica per un parametro integrity sull'header di risposta Link. L'esempio della bozza è un preload di uno stile, con crossorigin anonymous. Gli esempi della sezione 1, su script e su link stylesheet, usano lo stesso valore anonymous.
Se i byte non coincidono
La sezione 3.7 dice che il browser rifiuta di disegnare o di eseguire la risposta che fallisce il controllo, e restituisce un network error come definito in Fetch. Parte un evento di errore. La nota dice che chi vuole un'altra copia, per esempio non dal fornitore, può intercettare quell'evento e sostituire la risorsa. Non scrivo il gestore. Un'altra nota della sezione 3.3.4 dice che il browser può cambiare il risultato del confronto per preferenze, segnalibri o aggiunte di terze parti. Non ho misurato una di quelle aggiunte.
Senza CORS le due pagine non dicono la stessa cosa
La nota della sezione 3.3.4 dice che Subresource Integrity richiede CORS, e che usarlo senza CORS è un errore logico. Incoraggia il browser a scrivere un avviso in console. L'algoritmo che confronta i byte, nello stesso punto, non guarda il mode della richiesta.
MDN dice un'altra cosa: un browser non permette a una richiesta no-cors di usare l'impronta, quindi quella richiesta fallisce sempre. Aggiunge che l'attributo crossorigin va messo perché, senza, il caricamento da HTML parte in no-cors. Gli esempi usano il valore anonymous. Non ho aperto la definizione HTML di quel valore, né il passo di Fetch che rifiuterebbe la richiesta. Non ho unito la nota della bozza e la frase di MDN.
Il motivo, sezione 5.3, è che il successo o il fallimento del caricamento direbbe se i byte coincidono con un'impronta già scelta, anche quando la risposta non è stata condivisa. MDN descrive la stessa idea. Per questo il draft vuole che il contenuto sia condiviso in modo esplicito con CORS. Non descrivo i passi. MDN aggiunge che un fornitore spesso risponde con l'asterisco. Quando quell'asterisco fa leggere la risposta sta nella pagina sul CORS, già pubblicata. Non la riscrivo.
La pagina può esigere l'impronta
La sezione 3.8 definisce due header: Integrity-Policy e Integrity-Policy-Report-Only. Il valore è un dizionario di campi strutturati. Le destinazioni possibili sono solo script e style. L'unica sorgente possibile è inline. Se la chiave sources manca, oppure contiene inline, il parse aggiunge comunque inline. MDN dice la stessa cosa: omettere sources è simile a scriverlo. Le destinazioni entrano nella lista solo se la chiave blocked-destinations le nomina. Gli endpoint sono una lista di nomi, da dichiarare nell'header Reporting-Endpoints. Non ho aperto quell'header.
Il passo 3.8.2 lascia passare la richiesta se l'impronta capita non è vuota e il mode è cors oppure same-origin. Lascia passare anche un URL local: non ho aperto che cosa significhi local, quindi non elenco gli schemi. Se entrambe le policy sono vuote, lascia passare. Se il globale non è una finestra né un worker, lascia passare. Altrimenti blocca solo se le sorgenti contengono inline e la destinazione della richiesta è nella lista. Il report-only non blocca: segna il rapporto e la richiesta resta ammessa. Il rapporto ha tipo integrity-violation e porta l'indirizzo del documento, l'indirizzo bloccato, la destinazione e un booleano reportOnly.
MDN, nella frase sull'header che blocca, dice che il browser ferma le richieste no-cors e quelle senza impronta, e che manda il rapporto se c'è un endpoint. Nella tabella delle chiavi limita le destinazioni a script e style, come il draft. La frase breve non ripete che una destinazione assente dalla lista non viene bloccata. L'algoritmo sì. Non ho fuso le due frasi.
Esempio ipotetico: il foglio del fornitore
I nomi bottega.example e fornitore.example sono di scuola. Non ho calcolato un digest e non ho letto una risposta.

La pagina è https://bottega.example/scheda. Chiede https://fornitore.example/foglio.css con un link di stile, l'attributo integrity che inizia con sha384 e un digest di scuola, e crossorigin anonymous, come gli esempi della bozza. Se i byte, una volta applicata la funzione e codificati in base64, coincidono con quel digest nelle stesse maiuscole, la sezione 3.3.4 restituisce true. Se il fornitore cambia il foglio e il digest no, restituisce false, e la sezione 3.7 non applica il foglio.
Se accanto alla sha384 c'è anche una sha256, la sezione 3.3.3 non la usa. Se la sha384 non coincide, la sha256 non salva il foglio. Se ci sono due sha384 e una delle due coincide, il foglio entra. Se il nome fosse una funzione che il browser non ha nell'insieme, e non ci fosse altro, il parse resterebbe vuoto e il confronto restituirebbe true. È lo scenario delle pagine lette, non una misura.
Senza l'attributo crossorigin, MDN dice che la richiesta è no-cors e fallisce sempre. La nota della bozza la chiama un errore logico e chiede un avviso in console. Non ho unito le due frasi e non ho misurato un browser.
Il canale sicuro non fissa i byte
L'introduzione dice che TLS, HSTS e le chiavi fissate del server autenticano il server, non il contenuto. Chi può cambiare il file sul server può cambiarlo lo stesso. La sezione 5 non è normativa. La sezione 5.1 dice che, se la pagina non è un Secure Context, per esempio una pagina HTTP, un attaccante sulla rete può cambiare l'impronta lungo la strada, o toglierla. Il consiglio è di mandare l'impronta solo da un Secure Context. L'impronta non rende sicura una pagina che non lo è. Non ho aperto la definizione di Secure Context.
La sezione 5.2 dice che il digest è forte quanto la funzione, e che MD5 e SHA-1 non sono consigliate. Non stanno nell'insieme normativo della sezione 2. Al momento della scrittura, la stessa sezione indica SHA-384 come una buona base. È un consiglio, non una riga dell'algoritmo: sha256 resta obbligatoria per un browser conforme.
La sezione 4 dice che un proxy che modifica la risposta deve tenere il digest allineato ai byte nuovi, oppure consegnare solo la versione canonica del file per cui la pagina ha chiesto il controllo. Per avvisare gli intermediari, chi serve il file dovrebbe mandare Cache-Control con il valore no-transform. È un should, non un divieto che ho misurato.
Che cosa questa pagina lascia stare
Non ho aperto HTML, quindi non confermo modulepreload né il momento esatto in cui il documento chiama il confronto. Non ho aperto la Raccomandazione pubblicata. Non ho eseguito il comando che la bozza e MDN mostrano per calcolare il digest, e non cito quel comando. Non ho misurato un browser.
Fonte: Subresource Integrity, Editor's Draft, 20 marzo 2026, sezioni 1, 2, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 4 e 5, letta il 2 novembre 2026. Pagina MDN Subresource Integrity, ultima modifica 14 aprile 2026, letta lo stesso giorno. Non ho misurato un browser.
I due schemi nel testo sono disegni di questa pagina, non fotografie. La copertina non è stata cambiata.


