Angular subresource integrity

Subresource integrity is a browser feature that verifies that a resource, script or link element, has not been tampered with. Subresource integrity helps protecting your website from any resource manipulation that can happen especially if you are hosting your resources in a CDN where you are not in control of hosting the resources that you are using.

Through malicious javascript an attacker can get control of various informations of your users that are browsing your site such as: cookies, storage, form data etc. Thanks to subresource integrity you can control that no resource has being tampered directly in the browser.

You can use subresource integrity by adding the attribute integrity to any script and link tag. The integrity attribute accepts a string containing the suffix of the hash algorithm and followed by the base64 hash.

<script  
      type="text/javascript"
      src="runtime.js"
      integrity="sha384-l2idrIRRBrjHbp+jErEfqKMlALJ8XhHO2xWq/3wCMAE2uO8I7Wv+yulQip2yKsG+"
      crossorigin="anonymous">
</script>  

In order to generate a hash in OSX you can use shasum command line utility over the desired file and then pass the output to a series of transformations:

shasum -a 384 /path/file | awk '{ print $1 }' | xxd -r -p | base64  

This will output a hash which we can then use in our subresource integrity. Let's start with a simple example, creating a javascript file tegridy.js:

function tegridy() {  
 return 'No vaping';
} 

Now using shasum command as described above we will get the hash without the prefix:

shasum -a 384 tegridy.js | awk '{ print $1 }' | xxd -r -p | base64

>> e8LWRk7Tfo31vuZ4RTkVYgYunN58TLsbjxBkG7Qi/kikqRWXkwRInR2NB5ga0GUt

Once we have the hash we can add the integrity attribute in the script tag adding the prefix of the hash algorithm used:

<html>  
<title>Tegridy Farm</title>  
<body>  
<script  
  integrity="sha384-e8LWRk7Tfo31vuZ4RTkVYgYunN58TLsbjxBkG7Qi/kikqRWXkwRInR2NB5ga0GUt"
  src="./tegridy.js">
</script>  
<script>  
console.log(tegridy());  
</script>  
</body>  

The browser, before executing the script, will check the integrity of the script based on the hash passed as argument. If we then try to change the file tegridy.js slightly:

function tegridy() {  
 return 'Yes vaping';
}

The integrity of the file won't match with the attribute passed the browser then will throw a network error refusing to execute the script and indicating that fetching it failed.

subresource-integrity-error

Angular integrity

For simple applications where resources hardly change you can calculate the hash for each resource manually and attach to the element. While working on Angular application instead most of the javascript resources will be bundled by webpack at build time. A manual workflow is not exactly scalable since every time we will make a change to our application, angular-cli will create a new bundled resource which will need a new hash to be generated.

Thanks to the angular-cli team there is already an option to add the subresource integrity to your files by simply appending —subresource-integrity to your build script:

"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "ng build --subresource-integrity",
  "test": "ng test",
  "lint": "ng lint",
  "e2e": "ng e2e"
},

Once the flag is added, the cli will automatically generate a hash for the application resources and add to the index.html when running npm run build:

...
<script  
  type="text/javascript"
  src="runtime.js"
  integrity="sha384-l2idrIRRBrjHbp+jErEfqKMlALJ8XhHO2xWq/3wCMAE2uO8I7Wv+yulQip2yKsG+"
  crossorigin="anonymous">
</script>  
<script  
  type="text/javascript"
  src="es2015-polyfills.js"
  nomodule
  integrity="sha384-3g/ASsyNZi2bts3atqex3i7NlTDWAWh0/E+F1Y6dq80XjKjmW5f5LG7LThpiv4et"
  crossorigin="anonymous">
</script>  
...

This is especially useful when you are hosting all your application assets in another service like a CDN which sits outside of your organisation. Remember that you can start using subresource integrity even for third-party CDN hosted files. Popular CDN like unpkg and cdnjs already support it.