About Subresource Integrity (subresource integrity) If you don’t pay much attention to web security, you may not have heard of this term. I believe that after reading this article, you will be able to gain a deeper understanding of what SRI is, why you should use SRI, and how to practice using SRI in your projects if you have a need for it.

What SRI is and what it solves

SRI is shorthand for Subresource Integrity, which stands for subresource integrity. For example, for the style files we introduce in the page through the link and script tags or the third-party libraries we introduce to use are the subresources of the page. For example, something like the following.

1
2
3
4
5
6
<link rel="stylesheet" 
      href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"  
      crossorigin="anonymous">

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" 
        crossorigin="anonymous"></script>

In general, in order to improve the response speed as well as the performance of web pages, we usually put these sub-resources on CDN. For large Internet companies, they will generally have their own cloud services and also basically their own CDN services. But for smaller companies, they will generally use the CDN function provided by the cloud service vendor. Here comes a problem, if the resources we host on the cloud service vendor’s CDN are tampered with in case, then it will have some impact on our business. Although this kind of thing will not happen in general, if our business has high security requirements, then it is still important to take precautionary measures against this situation.

SRI is a solution to this problem. So what is the specific way to solve it? First of all, for a file, how do we know if the content of this file has been tampered with? We can perform a hash calculation' on the file and then generate a unique string associated with the content of the file by base64’ encoding. If the content of the file has changed, the string generated in the same way is not the same as the one generated by the original file. This way we know that the file has been tampered with. About this point should be relatively easy to understand if you know anything about blockchain.

For each introduced third-party resource, we just need to add the integrity attribute to the corresponding tag, and the value of the integrity attribute is a string of the form as follows.

1
2
3
<script src="https://example.com/example-framework.js"
        integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
        crossorigin="anonymous"></script>

where sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC is the value of integrity, a string starting with sha384, which indicates the name of the corresponding secure hashing algorithm, along with sha256 and sha512; followed by a short horizontal line -, which separates the name of the algorithm and the value of the base64 encoding generated by this algorithm later; the last oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC indicates the string that is generated after the corresponding file is calculated.

When a browser downloads a subresource with the integrity attribute, it will not immediately execute the code inside; or apply the style inside. The browser will first calculate whether the hash value of the file is the same as the one in the tag based on the corresponding algorithm specified in the value of the integrity attribute and the content of the downloaded file, and will apply the corresponding style or execute the corresponding code only if the two are the same. If the two are not the same, then the browser will refuse to execute the corresponding code, and refuse to apply the corresponding style. It will also report an error in the console, alerting us that there is a problem with the currently downloaded subresource.

In this way we ensure that our pages do not use resources downloaded from CDNs with tampered content by SRI this method. This ensures the security of our pages.

How to use SRI

The above briefly describes what SRI can do, so how do you practice it exactly? Let’s practice how to use SRI together.

First we create a random index.html and add the following content to it.

1
2
3
<script src="http://localhost:3000/test.js"
        integrity="sha384-yGduQba2SOt4PhcoqN6zsgbwhbpK8ZBguLWCSdnSRc6zY/MmfJEmBguDBXJpvXFg"
        crossorigin="anonymous"></script>

Then create a test.js file with the following contents.

1
document.write("Hello World!");

Then make test.js locally accessible via http://localhost:3000/test.js using the express framework of Node.js or some other tool.

For the integrity attribute of the script tag above, we can use the following command to obtain the corresponding sha384 algorithm generated string via the openssl tool.

1
cat test.js | openssl dgst -sha384 -binary | openssl base64 -A

In case of Windows environment, you need to use another way to get the corresponding string.

Then open index.html in your browser, you will see the page displaying: Hello World!. If we change the content of test.js at this time, on top of the original, remove the exclamation mark after Hello World!, as follows.

1
document.write("Hello World");

Then this time the page is blank and no longer shows Hello World. The corresponding console will also report an error, but the error message will be different for different browsers.

  • Chrome reports the following error.

  • Firefox报错如下:

  • Safari报错如下:

Anyway, you will be reminded that the currently downloaded subresource is not the same as the one on the tag through the calculated hash string, and the browser refuses to execute the corresponding code.

Here are some more points to note, if our test.js resource is a different source from our index.html, then you also need to add crossorigin="anonymous" to the tag, indicating that the request for this resource is required to be shared across the source resources. Otherwise the browser will report an error as follows.

If you don’t understand cross-origin resource sharing very well, you can refer to Cross-Origin Resource Sharing.

Of course, the corresponding server side also needs to set the corresponding response header: Access-Control-Allow-Origin: *, if you are using express, you can use cors to set it simply. The details are shown below

1
2
3
4
5
6
7
// ...
app.use(
    cors({
        origin: "*",
    })
);
// ...

How to use SRI in the framework

  • For Vue projects, we can use this feature very simply by using Vue CLI. By adding a configuration to vue.config.js: integrity: true, we can see after build time that the resources introduced in the packaged index.html are with the integrity attribute, as shown below.
1
2
3
4
5
6
7
8
9
<!-- ... -->
<link href="/css/app.fb0c6e1c.css" rel="stylesheet"
          integrity="sha384-1Ekc46o2fTK9DVGas4xXelFNSBIzgXeLlQlipQEqYUDHkR32K9dbpIkPwq+JK6cl">
<!-- ... -->
<script src="/js/chunk-vendors.0691b6c2.js"
        integrity="sha384-j7EDAmdSMZbkzJnbdSJdteOHi77fyFw7j6JeGYAf4O20/zAyQq1nJ91iweLs6NDd"></script>
<script src="/js/app.290d19ae.js"
        integrity="sha384-S3skbo1aIjA4WCmQH6ltlpwMgTXWrakI5+aloQEnNKpEKRfbNyy1eq6SrV88LGOh"></script>
<!-- ... -->
  • For other frameworks, if the packaging tool is Webpack, you can directly use the corresponding plugin webpack-subresource-integrity, the installation and usage instructions can be found here.

Some details about Integrity

In the actual use process, there are still many details that need to be paid attention to, so here’s a more in-depth introduction for you.

  • The algorithms currently used to calculate hashes of resource files are sha256, sha384, sha512, which are all secure hash algorithms belonging to SHA-2.

  • The algorithms for computing hash values using MD5 and SHA-1 are no longer recommended.

  • First of all Integrity values can exist more than one, using spaces to separate each value.

    • If multiple values are each using different secure hashing algorithms, such as the following.
    1
    2
    3
    4
    5
    6
    7
    
    <script src="http://localhost:3000/test.js"
            crossorigin="anonymous"
            integrity="
            sha256-LsK9lSOT7mZ9iEbLTm9cwaKTfuBdypNn2ID1Z9g7ZPM=
            sha384-yGduQba2SOt4PhcoqN6zsgbwhbpK8ZBguLWCSdnSRc6zY/MmfJEmBguDBXJpvXFg
            sha512-2qg2xR+0XgpsowJp3VCqWFgQalU9xPbqNTV0fdM9sV9ltHSSAcHni2Oo0Woo6aj860KvFu8S1Rdwb8oxJlMJ2Q==
    "></script>
    

    So this time the browser is processed according to that safe hashing algorithm? Or is it enough to have a match?

    The answer is:The browser will first choose the one with the highest security. If it is the above example, the browser will choose sha512, the algorithm that calculates the hash value. Because the security of sha512 is greater than sha384, and the security of sha384 is greater than sha256, and then it will ignore the rest of the hash values calculated by other means. At this time, it should be noted that if the hash string calculated by the browser according to sha512 is different from the one provided, then regardless of whether the hash value provided by sha384 or sha256 is correct, the browser will consider that the hash value calculated by this resource is not the same as the one provided. So the corresponding code will not be executed.

    • If multiple values are each using the same secure hash algorithm, for example, as follows.
    1
    2
    3
    4
    5
    6
    7
    
    <script src="http://localhost:3000/test.js"
            crossorigin="anonymous"
            integrity="
            sha384-yGduQba2SOt4PhcoqN6zsgbwhbpK8ZBguLWCSdnSRc6zY/MmfJEmBguDBXJpvXFg
              sha384-c+xXeW2CdZ1OuDKSrMpABg4MrVFWi3N5VKDC6CTgSRRnPr0dgprowjuFPomHgXlI
          sha384-E6ULLMoeKAMASZMjQ00AvU+3GzK8HPRhL/bM+P4JdcHLbNqGzU14K9ufSPJCnuex
    "></script>
    

    Then at this time, as long as there is a value that is the same as the result calculated by the browser, then the resource can be considered untampered with; the content of the resource can be executed.

  • The Integrity attribute only supports link and script tags for now, more tags about sub-resources will be supported later, such as: audio, embed, iframe, img, etc..