Integrity checking for JavaScript

Including JavaScript files from a CDN can be beneficial in many ways as you don’t have to ship the code with your code and caching can be done by the browser of a proxy server. It also allows for injecting untrusted code into a web page as someone else is hosting the code you rely on. But Firefox, Chrome and Opera already support Subresource Integrity checking script and link tags. Hopefully both Safari and Edge (or Internet Explorer) will support it soon.

But how does it work? First let calculate the SHA256 hash of JQuery version 3.2.1 hosted by CloudFlare. Also keep in mind to verify this number with the official version offered by JQuery. In this example we download the minimized version of JQuery with curl and run it twice through openssl to generate the checksum and encode the result in base64 format.

$ curl -s https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js | openssl dgst -sha256 -binary | openssl enc -base64 -A
hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=

Now that we have the hash we can add the integrity attribute to the script tag and the prefix for the hash is “sha256-” to indicate the hashing used. From this point forward a browser that supports SubResource Integrity will require that the provided hash will match the calculated hash of the downloaded file.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>

Beside SHA256 the specification allows for SHA384 and SHA512 to be used. Calculation is the same as with SHA256 and we only change the algorithm that openssl needs to use.

$ curl -s https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js | openssl dgst -sha512 -binary | openssl enc -base64 -A
3P8rXCuGJdNZOnUx/03c1jOTnMn3rP63nBip5gOP2qmUh5YAdVAvFZ1E+QLZZbC1rtMrQb+mah3AfYW11RUrWA==

We could put only the SHA512 hash in the attribute, but we can put multiple algorithm results in the same attribute by just splitting them with a space. This leaves a lot of room for proper lifecycle management of hashing algorithms as you can present multiple hashes when you switch to a better version instead of doing it big bang style and hope for the best.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4= sha512-3P8rXCuGJdNZOnUx/03c1jOTnMn3rP63nBip5gOP2qmUh5YAdVAvFZ1E+QLZZbC1rtMrQb+mah3AfYW11RUrWA==" crossorigin="anonymous"></script>

The next step is to have a fallback when the CDN you rely on goes down or is serving corrupt files. You could add a second src tag as in the example below that tells the browser to use the Google CDN when CloudFlare has issues serving the correct files.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" noncanonical-src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4= sha512-3P8rXCuGJdNZOnUx/03c1jOTnMn3rP63nBip5gOP2qmUh5YAdVAvFZ1E+QLZZbC1rtMrQb+mah3AfYW11RUrWA==" crossorigin="anonymous"></script>

The next step is to get the Content-Security-Policy header correct, but for now only Firefox 49 and higher have the option to act on the require-sri-for attribute. This would basically force the browser to only load scripts and style sheets if the SRI-steps are successful, but many a lot of developers need to optimise their build pipeline to produce correct hashes and have correct monitoring to detect problems.

Debian mirrors volgens het CDN-model

Bij het lezen van idee #25103 op de Ubuntu Brainstorm-site kwam de vraag hoever Debian dit nu heeft opgelost. Zeker aangezien Debian de moederdistributie van Ubuntu is en ze enige tijd geleden al waren overgegaan op een GeoDNS oplossing voor sommige sites.

Een brand, een zware verstoring en een update van OpenOffice.org heeft ervoor gezorgd dat model van security.debian.org anders moest. Het moest van een enkele server naar een schaalbare oplossing gaan zonder daar de gebruikers mee op te zadelen. Dit is gelukt aangezien security.debian.org nu mbv GeoDNS en HTTP-polling voor iedereen bereikbaar is gemaakt door een bereikbare mirrorserver in de buurt van het gebruiker aan te bieden.

Voor de algemene repository is de urgentie wat minder groot, omdat de gebruiker zelf kan kiezen in welk land de mirrorservers moeten worden benaderd. De vraag blijft of deze manier blijft schalen, want er zal altijd wel ergens een server onbereikbaar zijn door werkzaamheden. De Nederlandse mirror is daar geen uitzondering van, maar voor gratis hosting moet je iets over hebben. En waarom zou een andere server dit werk niet “transparant” kunnen overnemen?

Een klein project loopt nu om te testen of te kijken wat de meest ideale manier is om dit ook voor de normale mirrors van de repository te doen. Gelijk komt hier het probleem naar voren dat niet alle mirrors alle architecturen aanbieden, maar iedereen met een i386 of amd64 installatie zou overal zijn packages moeten kunnen krijgen. De aanpassing aan sources.list zoals hieronder is vrij simpel.

deb http://cdn.debian.net/debian/ testing main
deb-src http://cdn.debian.net/debian/ testing main
deb http://security.debian.org/ testing/updates main
deb-src http://security.debian.org/ testing/updates main

Nu al enige tijd staat cdn.debian.net in mijn configuratie voor APT en ik moet zeggen dat ik voorlopig tevreden ben. Hopelijk wordt dit project verder uitgewerkt en opgenomen in zeg Debian 7.0, maar dan moet 6.0 wel eerst uitkomen natuurlijk.