A CDN that can not XSS you: Using Subresource Integrity

This blog post is the text-version of my presentation from OWASP AppSec EU 2015. You can download the slides or watch the video on YouTube

Introduction

In this blog post, I explain Subresource Integrity (SRI), of which I am one of the co-editors. SRI is an upcoming W3C standard that allows securing third-party loads of JavaScript and CSS. This is achieved by comparing the content with a cryptograhic digest that is contained within the surrounding HTML tag.

Motivation

Many websites use Content Delivery Networks (CDNs) to improve website performance and save on bandwidth. Loading a popular libraries in a common version from a CDN has a high likelihood of being already in your visitor's cache. A lot of CDNs also provide metrics.

The problem I see with CDNs is that you are increasing the risk surface of your web application: If your own setup is relatively secure, the attacker may just go ahead and attack the CDN instead.

This is because JavaScript files included on your HTML execute within the scope of your application: This spans everything that is on the same scheme (HTTP or HTTPS), domain name and port (i.e., on the same origin. See here for more). This implies not only access to all web content, which may be sensitive information like user names, passwords and email address. The script is also allowed to fully interact with the DOM, the cookies and all permissions given to the current website (e.g., Geolocation, Notifications, etc.).

Furthermore, attackers that are interested in targeting a lot of users (and not just your web application) will go for popular CDNs to gain a very high impact - as we have seen with DDoS attacks against GitHub.

But reducing this attack surface is not that complicated In fact, we already know exactly what file we want to include. There is no need to allow arbitrary scripts from a third-party to go wild within your web page. If only we could check the file before executing it.

Subresource Integrity to the Rescue

You can now check that the scripts matches exactly what you wanted to include, when you originally wrote your web page!

Subresource Integrity (SRI) allows specifying the digest of the file that you want to include. The digest is the output of a cryptographic hash function, which helps us achieve integrity.

The idea is that you provide a short integrity attribute, which will help the browser decide if the file has been modified. This works because a hash function gives a completely different output every time someone modifies the file.

Using Subresource Integrity

Subresource Integrity is an upcoming W3C standard to secure script and style loads from other domains.

Just add an integrity attribute to your script tag.

Scripts
<script src="https://code.jquery.com/jquery-1.10.2.min.js"
        integrity="sha256-C6CB9UYIS9UJeqinPHWTHVqh/E1uhG5Twh+Y5qFQmYg="
        crossorigin="anonymous"></script>
Styles
<link rel="stylesheet" href="https://site53.example.net/style.css"
      integrity="sha256-vjnUh7+rXHH2lg/5vDY8032ftNVCIEC21vL6szrVw9M="
      crossorigin="anonymous">

Computing the integrity value

The value of the integrity attribute forms a tiny micro-syntax, that is the name of the cryptographic hash function (e.g. sha256) and the output that it has given us.

An example is given in the SRI specification:

$ echo -n "alert('Hello, world.');" | openssl dgst -sha256 -binary | openssl enc -base64 -A
qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=

Just prefix this with sha256- and you're done.

What is this crossorigin attribute?

It is usually not allowed to read the content of files on other domains (rather: origins). It only works if the domain explicitly allows this. In short, SRI requires CORS to work. The crossorigin attribute tells the browser to fetch the file in a way that allows reading it afterwards or fail if CORS is not supported. Fortunately, a lot of CDNs already enable cors.

Multiple hashes

You can provide multiple tokens of integrity metadata (i.e., hashname, dash and value) in one integrity attribute. If they contain different hash functions, the browser will pick and prioritize the strongest.

You can also specify multiple values with the same hash function. This is useful when you expect one of many possible resources behind a URL. This can happen because of browser sniffing, or when you expect an update to a file that should not break the page.

Error Recovery & Reporting

Despite earlier attempts, there is no error recovery or error reporting functionality in Subresource Integrity for now.

You need to recover from failed script loads, otherwise your website may break. A typical attempt would be to load the desired third-party library from an (un-cached) copy on your own origin:

<script src="https://code.jquery.com/jquery.min.js"
        integrity="sha256-C6CB9UYIS9UJeqinPHWTHVq…"
        crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="/jquery-.min.js"><\/script>')</script>

You could also use navigator.sendBeacon to log this, if you have an infrastructure for error reporting.

Tooling

There are already some tools that help using Subresource Integrity.

Implementation Status

Subresource Integrity is coming to mainline browsers soon (Firefox, Chrome or use a Polyfill). You can test SRI support on srihash.org.

Conclusion

Loading your scripts and styles from a Content Delivery Network (CDN) can harm your website, if the CDN gets compromised or becomes malicious. You can add integrity attributes to secure your third-party scripts & styles. This greatly diminishes security risks, as any change to those files will be detected and prevented by the browser.


If you find a mistake in this article, you can submit a pull request on GitHub.

Other posts

  1. Prompt Injections and a demo (Wed 18 September 2024)
  2. The Mozilla Monument in San Francisco (Fri 05 July 2024)
  3. What is mixed content? (Sat 15 June 2024)
  4. How I got a new domain name (Sat 15 June 2024)
  5. How Firefox gives special permissions to some domains (Fri 02 February 2024)
  6. Examine Firefox Inter-Process Communication using JavaScript in 2023 (Mon 17 April 2023)
  7. Origins, Sites and other Terminologies (Sat 14 January 2023)
  8. Finding and Fixing DOM-based XSS with Static Analysis (Mon 02 January 2023)
  9. DOM Clobbering (Mon 12 December 2022)
  10. Neue Methoden für Cross-Origin Isolation: Resource, Opener & Embedding Policies mit COOP, COEP, CORP und CORB (Thu 10 November 2022)
  11. Reference Sheet for Principals in Mozilla Code (Mon 03 August 2020)
  12. Hardening Firefox against Injection Attacks – The Technical Details (Tue 07 July 2020)
  13. Understanding Web Security Checks in Firefox (Part 1) (Wed 10 June 2020)
  14. Help Test Firefox's built-in HTML Sanitizer to protect against UXSS bugs (Fri 06 December 2019)
  15. Remote Code Execution in Firefox beyond memory corruptions (Sun 29 September 2019)
  16. XSS in The Digital #ClimateStrike Widget (Mon 23 September 2019)
  17. Chrome switching the XSSAuditor to filter mode re-enables old attack (Fri 10 May 2019)
  18. Challenge Write-up: Subresource Integrity in Service Workers (Sat 25 March 2017)
  19. Finding the SqueezeBox Radio Default SSH Password (Fri 02 September 2016)
  20. New CSP directive to make Subresource Integrity mandatory (`require-sri-for`) (Thu 02 June 2016)
  21. Firefox OS apps and beyond (Tue 12 April 2016)
  22. Teacher's Pinboard Write-up (Wed 02 December 2015)
  23. A CDN that can not XSS you: Using Subresource Integrity (Sun 19 July 2015)
  24. The Twitter Gazebo (Sat 18 July 2015)
  25. German Firefox 1.0 ad (OCR) (Sun 09 November 2014)
  26. My thoughts on Tor appliances (Tue 14 October 2014)
  27. Subresource Integrity (Sun 05 October 2014)
  28. Revoke App Permissions on Firefox OS (Sun 24 August 2014)
  29. (Self) XSS at Mozilla's internal Phonebook (Fri 23 May 2014)
  30. Tales of Python's Encoding (Mon 17 March 2014)
  31. On the X-Frame-Options Security Header (Thu 12 December 2013)
  32. html2dom (Tue 24 September 2013)
  33. Security Review: HTML sanitizer in Thunderbird (Mon 22 July 2013)
  34. Week 29 2013 (Sun 21 July 2013)
  35. The First Post (Tue 16 July 2013)
π