Hoppa till innehållet

Subresource Integrity

ett hashvärde som en webbläsare kontrollerar en fil mot

Vad det är

Subresource Integrity låter en sida i förväg ange exakt vilka byte ett skript eller en stilmall måste innehålla, och får webbläsaren att kasta filen om det som kommer fram är något annat. Namnet förkortas oftast SRI. Sidan skriver ett kryptografiskt hashvärde av filen i ett integrity-attribut på det script- eller link-element som läser in den. När svaret kommer kör webbläsaren samma hashfunktion över de byte den tog emot och jämför resultatet med hashvärdet på elementet. Om de är lika körs filen, eller formar sidan, som vanligt. Om de skiljer sig behandlar webbläsaren svaret som ett nätverksfel: skriptet körs aldrig och stilmallen tillämpas aldrig.

Värdet består av namnet på en hashfunktion, ett bindestreck och hashvärdet i base64. Ett enda kommando över den färdiga filen tar fram det, och att köra det två gånger visar hur lite som krävs för att ändra svaret:

$ printf "alert('Hello, world.');" > hello.js
$ openssl dgst -sha384 -binary hello.js | openssl base64 -A
H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO
$ echo >> hello.js
$ openssl dgst -sha384 -binary hello.js | openssl base64 -A
qu8P7hCLFcBpas8BOnGmaANCkfXJIpFttDEaxOj0Pq9H6Q8NYjsMb+PVqwuJygVe

Med sha384- framför är den första utskriften attributets värde för det enradiga skriptet. En enda radbrytning som läggs till på slutet ger ett hashvärde som inte har något gemensamt med det första, och en webbläsare som har det första skulle vägra den andra filen.

Specifikationen känner igen tre hashfunktioner, SHA-256, SHA-384 och SHA-512, alla från familjen SHA-2, och kallar SHA-384 en bra utgångspunkt vid tiden då den skrevs. Ett attribut får lista flera hashvärden åtskilda av mellanslag. Webbläsaren väljer den starkaste funktionen bland dem och godtar filen när den stämmer med något av de hashvärden som gjorts med den funktionen. Det låter en sida gå över till en starkare algoritm, eller godta två versioner av ett bibliotek under ett byte, utan ett fönster där sidan bara bär felaktiga hashvärden.

Attributet finns på script och link, och samma värde kan följa med i ett Link-svarshuvud som läser in en fil i förväg. Specifikationens nuvarande text är dess andra utgåva, ett W3C Working Draft från Web Application Security Working Group; den första utgåvan blev en W3C Recommendation 2016.

Varför det spelar roll

En sida som läser in ett bibliotek från ett CDN, eller från någon värd som dess ägare inte driver, ger den värden makt att köra kod på sidan. HTTPS skyddar filen på vägen över nätverket, men säger ingenting om vad värden valde att skicka från början. Om någon bryter sig in i lagringen hos ett CDN, tar över ett konto som publicerar dit eller i tysthet byter ut filen bakom en välbekant adress, kör varje sida som inkluderar den den nya koden med sidans fulla befogenheter. Sidans egen kod har inget sätt att se skillnaden.

SRI flyttar tillbaka det beslutet till sidan. Hashvärdet räknas fram över en fil som webbplatsens ägare har läst eller byggt, och det följer med inuti sidan, från webbplatsens egen server. En värd som skickar något annat får sin fil avvisad i stället för körd. Sidan förlorar en funktion tills någon tittar efter, ett synligt fel med en uppenbar orsak, i stället för att i tysthet köra någon annans kod.

Hashvärdet låser också en version. En adress som /lib/latest.js kan när som helst börja leverera en ny version under en sida; hashvärdet kan inte följa med. En uppgradering som ägaren aldrig valde blir ett avvisande på ett bestämt element, rapporterat genom en felhändelse.

Det lönar sig att vara exakt om vad SRI inte gör. Det kontrollerar en fil mot ett hashvärde; det bedömer inte om filen är säker. Ett hashvärde av ett bibliotek som redan innehåller en fientlig rad skyddar den fientliga raden troget. Det lämnar inbäddade skript i fred, vilket är uppgiften för en Content Security Policy. Och det litar på sidan som bär hashvärdet, så sidan själv måste fortfarande komma fram oskadd.

Så ser det ut i praktiken

Den här webbplatsen använder det på sin egen stilmall. Säkerhetssidan listar Subresource Integrity på stilmallen: paketet får ett fingeravtryck och en hash när webbplatsen byggs, så att läsarens webbläsare kan kontrollera att det kom fram oförändrat. Samma lista visar varför arrangemanget förblir litet. Det finns ett eget skript på under en kilobyte, det finns inga anrop till tredje part, och en läsare som räknar värdarna i webbläsarens nätverkspanel hittar en enda, webbplatsens egen.

Kolofonen beskriver samma fil från byggets sida, som ett enda CSS-paket som sammanfogas och får ett fingeravtryck när webbplatsen byggs och levereras med en SRI-hash. Fingeravtrycket lägger in en hash av filens innehåll i dess namn, så att en ändrad stilmall får en ny adress och en webbläsare aldrig tar en gammal kopia i cachen för den nuvarande, medan integritetsvärdet låter webbläsaren kontrollera de byte som finns bakom adressen. Bygget tar fram båda ur samma byte i samma steg, så varje release levererar stilmallen och dess hashvärde tillsammans, och det vanligaste felet som beskrivs nedan, en fil som ändras efter att den hashades, har mycket litet utrymme att uppstå.

Var det går fel

Något mellan bygget och webbläsaren skriver om filen. Ett CDN som minifierar skript, en proxy som komprimerar om eller injicerar i svar, eller ett steg i driftsättningen som körs efter att hashvärdet togs ändrar alla de byte som levereras, och webbläsaren avvisar resultatet fast ingen har angripit något. Specifikationen lägger ansvaret på de mellanhänderna: de måste hålla hashvärdet i takt med det de levererar, eller leverera originalet orört, och ursprunget bör skicka Cache-Control: no-transform för att be dem lämna filen i fred. Den pålitliga ordningen är att ta hashvärdet sist, över exakt de byte som kommer att levereras.

En fil från ett annat ursprung kommer fram utan CORS. Specifikationen kräver att en integritetskontroll av en fil från ett annat ursprung använder CORS, och kallar ett försök utan det för ett logiskt fel, så elementet behöver crossorigin="anonymous" och värden måste svara med ett huvud som delar filen. Utelämna någon av dem och webbläsaren blockerar filen i stället för att läsa in den okontrollerad. Regeln skyddar andra webbplatser: utan den skulle en angripare kunna läsa in ett privat svar med ett gissat hashvärde och av utfallet lära sig t.ex. om läsaren är inloggad där.

Hashvärdet kommer fram över okrypterad HTTP. SRI skyddar en sida mot en komprometterad filvärd, aldrig mot någon på vägen genom nätverket. På en sida som levereras utan HTTPS kan den som kan skriva om filen under överföringen lika gärna skriva om eller ta bort hashvärdet i sidan, och därför rekommenderar specifikationen att metadata för integritet bara levereras till en säker kontext.

Hashfunktionen är en som webbläsaren hoppar över. Specifikationen känner bara igen SHA-256, SHA-384 och SHA-512 och rekommenderar att webbläsare vägrar kända svaga funktioner som MD5 och SHA-1. Överraskningen är vad som händer med en token utanför den listan: webbläsaren ignorerar den, och ett attribut som bara består av ignorerade token räknas som om det inte bar någon metadata alls, så filen läses in okontrollerad. Ett byggverktyg som kan ta fingeravtryck med MD5, så som verktyget i Hugo kan, gör det lätt att leverera det av misstag.

En avvisad fil drar med sig sidan. När kontrollen fallerar avfyrar webbläsaren en felhändelse på elementet, och skriptet eller stilmallen saknas helt enkelt. För ett bibliotek som sidan inte kan köras utan föreslår specifikationen att fånga händelsen och läsa in en reservkopia från en betrodd, om än långsammare, källa som webbplatsens egen server. Utan en sådan fungerar skyddet precis som det är tänkt, och läsaren får ändå en trasig sida.

Kontrollen gäller bara de element som bär ett hashvärde. Ett integritetsattribut på ett skript säger ingenting om nästa skript som någon lägger till utan det, och att granska de skript som har ett kommer aldrig att avslöja det som saknar det. Utkastet lägger till ett svarshuvud som täcker det från serversidan. Integrity-Policy med blocked-destinations=(script) blockerar varje externt skript som kommer fram utan metadata för integritet, och Integrity-Policy-Report-Only listar dem först, så att de kan rättas innan något blockeras.

Läs mer

Ordlista