The Importance of Verifying Third-party Scripts
It is commonplace for websites to load third-party scripts to add additional functionality. This can be to load libraries from CDNs or include tracking scripts. However, by doing so we are handing over some control of our sites to others.
What happens if one of those scripts, outside our control, changes?
A true story
Last month, a client got in touch concerned that malware had been found on their site. Malware is software that is specifically designed to disrupt, damage or gain unauthorised access to parts of your computer. This is definitely not something you want to find.
The suspected malware meant some of their users were potentially at risk. Additionally, the client's Google Ads were being rejected as a result of malware being detected. This was affecting their ability to market their product.
We were able to quickly identify that the client's site hadn't been hacked; however, one of the third-party tracking scripts was no longer delivering what it should.
It turned out that the third-party had let their script domain expire. The domain then became available for anyone to buy, which someone did. The new domain owner then used it to respond to script requests with a malware response.
This story highlighted the importance of verifying third-party resources. To do this we can use the Subresource Integrity (SRI) security feature built into web browsers. It is used to verify that fetched resources haven't been manipulated and became widely supported by browsers about 5 years ago.
How it works
When including a
<link> tag to an external resource, we include an
integrity attribute. The value of this attribute is a base64-encoded cryptographic hash of the resource (file) we are linking to. The hash is essentially a unique fingerprint associated with the resource.
When someone visits a page, the browser will check for any
<link> elements with an
integrity attribute. For these, it will run a check of the hash before executing a script or applying any styling. The
integrity hash must match the hash of the fetched resource; if they differ, then the resource has changed from what was expected and the browser will refuse to execute the script or apply the stylesheet.
If someone was to maliciously manipulate a resource, the resource will have changed and the hash will differ from the expected value. Our users will be protected from any harmful behaviour the resource may have exposed them to.
When using the
integrity attribute for an external resource browsers will also check the resource using Cross-Origin Resource Sharing (CORS). This is to check the server delivering the resource allows it to be shared with our site. Therefore, we also need to include
crossorigin="anonymous" alongside our
Generating SRI hashes
The simplest way to generate an SRI hash is using an online tool like the SRI Hash Generator.
<script src="https://firstname.lastname@example.org/dist/cdn.min.js" integrity="sha384-cNc6Ohk6WSbK+sByRXr9COZWTDVEag1x/Qb7jg15rtdQ4eOqYCfzwQGAxvoh+FQX" crossorigin="anonymous"></script>
The SRI hash is set as the value of the
integrity attribute. It is made up of two parts:
cNc6Ohk6WSbK+sByRXr9COZWTDVEag1x/Qb7jg15rtdQ4eOqYCfzwQGAxvoh+FQX- the base64-encoded 'hash' part
sha384- the prefix identifying the type of hash algorithm used
If we were to modify the hash, or the contents of
https://email@example.com/dist/cdn.min.js changed, then the browser will not run the AlpineJS library. In the browser's developer tools we would see a network error indicating that it failed to fetch the resource.
There is one limitation of using subresource integrity. If our resources are likely to change, then we won't be able to set the
integrity attribute, otherwise our site will stop properly functioning once the resource is updated. This is particularly problematic if we use non versioned resources. This will be the case if we use many of the Google services like Google Analytics or Google Tag Manager.
Check if the provider offers versioned resources and use those with subresource integrity where possible. In our example above, the CDN does provide mutable versions of AlpineJS, but it is better to use a tried and tested version that we can lockdown in our code.
If the provider doesn't provide immutable resources, there's not much we can do about it. We have to determine if the risk of using the resource is justifiable.
It's important we use trustworthy sources regardless of whether or not we can use subresource integrity. If we can't then it's extra important. Big providers like Google are likely to have many safeguards in place to prevent malicious code making its way into their services.
There is a real risk when using third-party resources to provide additional functionality to our webpages. However, subresource integrity provides a widely supported means of locking down the expected code which will safeguard our users from malware.
Example code for including third-party resources with
<link> often leave out the
crossorigin attributes. We need to be alert to this and ensure we include them wherever possible.
Get in touch
Named 2023 Large Dev Agency of the Year, we provide industry-leading web development services both for fortune-changing new projects and existing systems support. For more information, see our Build services.