Subresource Integrity TagHelper Using ASP.NET Core - Part 1

Make Security-Easy
  1. Subresource Integrity TagHelper Using ASP.NET Core - Part 1
  2. Subresource Integrity TagHelper Using ASP.NET Core - Part 2

What is Subresource Integrity (SRI)

Can you trust your CDN provider? What if they get hacked and the copy of jQuery you are using hosted by them has some malicious script added to it? You would have no idea this was happening! This is where Subresource Integrity (SRI) comes in.

It works by taking a cryptographic hash of the file hosted on the CDN and adding that to your script or link tags. So in our case if we are using jQuery, we would add an integrity and crossorigin attribute to our script tag like so:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js" 
        integrity="sha256-ivk71nXhz9nsyFDoYoGf2sbjrR9ddh+XDkCcfZxjvcM=" 
        crossorigin="anonymous"></script>

The cryptographic hashing algorithm used can be SHA256, SHA384 or SHA512 at the time of writing. In fact, you can use more than one at a time and browsers will pick the most secure one to check the file against.

The current official standard document states that currently only script or link tags are supported for your JavaScript or CSS. However, it also states that this is likely to be expanded to pretty much any tag with a src or href attribute such as images, objects etc.

Scott Helme has a great post on the subject which I highly recommend you read (It's where I learned about it).

The ASP.NET Core Tag Helper

I implemented a tag helper for ASP.NET Core which is as simple to use as this:

<script asp-subresource-integrity
        src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

Don't you love it when security is so easy! I'm a big believer in making security as easy as having a big red button that says 'on' and turning it on by default so people don't have to. It's the only way these things will get used! What is it doing behind the scenes?

  1. Downloads the file from the CDN.
  2. Calculates a SHA512 hash for the file.
  3. Adds the integrity and crossorigin attributes to the script tag.
  4. Adds the SHA512 hash value to the distributed cache (IDistributedCache) built in to ASP.NET Core with no expiry date. If you are using a distributed cache like Redis (Which you should for the pure speed of it) then the hash will be remembered.
  5. The next time the page loads, the hash is retrieved from the cache, so there is very little performance impact of this tag helper.

There are actually two tag helpers, one supports any tag with a src attribute and another supports any tag with a href element. This is in preparation for when subresource integrity is opened up to tags other than script and link.

Gotchas

In the past, I have often omitted the scheme from the CDN URL like so:

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>

However, I have noticed that Firefox, does not like it when you use SRI and omit the scheme. It stops the file from loading completely. When you think about it, this makes sense. We are trying to confirm that the resource has not been changed, one of the ways to do this is to use HTTPS. It does not make sense to use SRI over HTTP.

The other gotcha I found is that the resource must have the Access-Control-Allow-Origin HTTP header. It can be set to * or your individual domain name. Now, I have been using CDN resources provided by Google (for jQuery), Microsoft (for Bootstrap, jQuery Validation etc.) and MaxCDN (for Font Awesome) because they are free, most browsers have probably already got a copy of the files from there and because they have very fast global exit nodes.

However, I have discovered that all provide the Access-Control-Allow-Origin HTTP header except Microsoft on some of their resources. Strangely, they return the header for Bootstrap but not for the jQuery Validation scripts. I have reached out to them through my capacity as an MVP and hope to get the issue solved. In the mean time, if you are using Microsoft's CDN you can switch to another CDN or wait for them to fix the issue.

Where Can I Get It?

This tag helper is available in a few ways:

  1. The .NET Boxed Boxed.AspNetCore.TagHelpers NuGet package.
  2. Check out source code in the .NET Boxed Framework GitHub repository.

Web Mentions

What's this?

0 Replies

GitHub Comments

RehanSaeed
(edited )

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2016-03-07 08:55:43

It isn't just CDN's that get hacked. My mobile ISP (optus) had their dns hijacked, which served a hacked version of ga.js.

http://forums.whirlpool.net.au/archive/2479474

That's a great example Justin! It's not just ISP's getting hacked though. Sometimes the ISP's are the ones deliberately doing the hacking in authoritarian regimes. In those cases, Content Security Policy (CSP) saves the day in most cases. Security is all about layers, the more layers you have the safer you will be and Subresource Integrity (SRI) is just one layer in the cake.

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2016-03-08 07:33:57

You can confirm the presence of SRI hashes on your website with: https://sritest.io
Disclaimer: I am the developer of this service

Cool service. Does it check that the URL has a scheme? Firefox has different behaviour when it doesn't. How would you feel about me recommending your service to check SRI's?

RehanSaeed

Gabor Szathmari Gabor Szathmari commented on 2016-03-08 11:58:10

Cool service. Does it check that the URL has a scheme? Firefox has different behaviour when it doesn't. How would you feel about me recommending your service to check SRI's?

Sritest.io works with http:// as well https:// as URLs. Does Firefox check SRI differently depending on the URL scheme?
I would be happy if you wrote something about the service.

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2016-03-08 12:07:42

Sritest.io works with http:// as well https:// as URLs. Does Firefox check SRI differently depending on the URL scheme?
I would be happy if you wrote something about the service.

In my testing having no scheme e.g. //foo.cdn/jquery caused an error in Firefox which stopped the resource loading completely, so perhaps your service should raise a warning/error for this. I did not try http:// in my testing but I would suggest that your service raises a warning/error in this case also, because having a secure connection will act as an added protection.

RehanSaeed

Gabor Szathmari Gabor Szathmari commented on 2016-03-08 12:09:24

In my testing having no scheme e.g. //foo.cdn/jquery caused an error in Firefox which stopped the resource loading completely, so perhaps your service should raise a warning/error for this. I did not try http:// in my testing but I would suggest that your service raises a warning/error in this case also, because having a secure connection will act as an added protection.

Hmmm interesting, I was not aware. I will look into that.

RehanSaeed

Gabor Szathmari Gabor Szathmari commented on 2016-03-10 00:27:49

In my testing having no scheme e.g. //foo.cdn/jquery caused an error in Firefox which stopped the resource loading completely, so perhaps your service should raise a warning/error for this. I did not try http:// in my testing but I would suggest that your service raises a warning/error in this case also, because having a secure connection will act as an added protection.

I created a test html file with // as the URL scheme, and Firefox loaded the page without errors.
Could you please have a look and take me a screenshot perhaps? The file is hosted on http://www.himalaya-taska.hu/sritest.html and https://www.himalaya-taska.hu/sritest.html I am running Firefox 44.0.2

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2016-03-10 09:19:47

I created a test html file with // as the URL scheme, and Firefox loaded the page without errors.
Could you please have a look and take me a screenshot perhaps? The file is hosted on http://www.himalaya-taska.hu/sritest.html and https://www.himalaya-taska.hu/sritest.html I am running Firefox 44.0.2

When navigating to http://www.himalaya-taska.hu/sritest.html, I get the following warning and then error in Firefox 44.0.2:

Warning: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
Error: None of the "sha384" hashes in the integrity attribute match the content of the subresource.

Perhaps this is one of my extensions causing the error. I'm using UBlock Origin, Ghostery and HTTPS-Everywhere because I'm paranoid and don't like advertisements.

RehanSaeed

Duncan Duncan commented on 2016-03-11 07:48:03

If the CDN gets hacked then ... won't this then start serving up hashes for the hacked content?

RehanSaeed

Gabor Szathmari Gabor Szathmari commented on 2016-03-11 08:30:29

In my testing having no scheme e.g. //foo.cdn/jquery caused an error in Firefox which stopped the resource loading completely, so perhaps your service should raise a warning/error for this. I did not try http:// in my testing but I would suggest that your service raises a warning/error in this case also, because having a secure connection will act as an added protection.

Possibly it is the extensions, I also had similar issues with them earlier.

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2016-03-11 08:43:36

Possibly it is the extensions, I also had similar issues with them earlier.

Yes, I think so too. I think it's safer to add https at the start. Perhaps your site could raise a warning about this?

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2016-03-11 08:44:52

If the CDN gets hacked then ... won't this then start serving up hashes for the hacked content?

My initial thinking was that you could check the files at deployment time when the tag helper first runs. Then the tag helper would have calculated the hash and cached it without any expiration time, so you are good from then on. That's still more secure than 'every site on the internet' as Jon Galloway put it in the ASP.NET standup.

Here is a link to a discussion about a further improvement to this tag helper which I plan to release this weekend.

RehanSaeed

Gabor Szathmari Gabor Szathmari commented on 2016-03-11 08:49:01

In my testing having no scheme e.g. //foo.cdn/jquery caused an error in Firefox which stopped the resource loading completely, so perhaps your service should raise a warning/error for this. I did not try http:// in my testing but I would suggest that your service raises a warning/error in this case also, because having a secure connection will act as an added protection.

I will have a look, I appreciate your feedback.

RehanSaeed

Duncan Duncan commented on 2016-03-11 09:44:06

My initial thinking was that you could check the files at deployment time when the tag helper first runs. Then the tag helper would have calculated the hash and cached it without any expiration time, so you are good from then on. That's still more secure than 'every site on the internet' as Jon Galloway put it in the ASP.NET standup.

Here is a link to a discussion about a further improvement to this tag helper which I plan to release this weekend.

I think it's a build-time concern. Possibly have a build step that calculates the SHAs and writes them to a known file location then the tag helper looks for them first before falling back to calculating on the fly.

RehanSaeed

Chris Pratt Chris Pratt commented on 2018-08-27 17:05:46

If the CDN gets hacked then ... won't this then start serving up hashes for the hacked content?

Duncan makes a good point. Potentially, you could allow hacked resources in this way. However, given that the SRI hash is cached indefinitely, and assuming you're using a reasonably persistent cache store (basically anything other than in-memory), the odds that the hash would be calculated at the exact point that the resource was compromised (which itself is a pretty wild occurrence, if you're using one of the big global CDNs) are pretty high. It's important to be aware of, but I doubt it would actually be a true security concern.

That said, this method could probably be improved by comparing to a local copy of the resource. ASP.NET Core already has functionality for providing fallback local scripts, should CDN resources fail. This same fallback could be used to compare with the remote version and a mismatch could be either logged or raised as an exception.

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2018-08-28 09:24:54

Duncan makes a good point. Potentially, you could allow hacked resources in this way. However, given that the SRI hash is cached indefinitely, and assuming you're using a reasonably persistent cache store (basically anything other than in-memory), the odds that the hash would be calculated at the exact point that the resource was compromised (which itself is a pretty wild occurrence, if you're using one of the big global CDNs) are pretty high. It's important to be aware of, but I doubt it would actually be a true security concern.

That said, this method could probably be improved by comparing to a local copy of the resource. ASP.NET Core already has functionality for providing fallback local scripts, should CDN resources fail. This same fallback could be used to compare with the remote version and a mismatch could be either logged or raised as an exception.

Take a look at part 2 of this post. That basically does what you suggest.

samthrusha123

Hi @RehanSaeed ,
Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.Infrastructure.IActionContextAccessor' while attempting to activate 'Boxed.AspNetCore.TagHelpers.SrcSubresourceIntegrityTagHelper'.

I'm getting the below exception while using the tag helper in asp.net core application
Can any one help??

RehanSaeed

Hi @RehanSaeed , Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.Infrastructure.IActionContextAccessor' while attempting to activate 'Boxed.AspNetCore.TagHelpers.SrcSubresourceIntegrityTagHelper'.

I'm getting the below exception while using the tag helper in asp.net core application Can any one help??

You need to register IActionContextAccessor like so:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();