DEV Community

Orkhan Huseynli
Orkhan Huseynli

Posted on

Verifying Integrity of Files using NodeJS

While using third party JavaScript or CSS libraries from CDN, you've probably came across with integrity attribute on script or link tags.

<script 
  src="https://code.jquery.com/jquery-3.6.4.min.js" 
  integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" 
  crossorigin="anonymous">
</script>
Enter fullscreen mode Exit fullscreen mode

If you haven't researched about them till now, then you're in the right place.

Man in the Middle Attacks

It's possible that the any data which travels through internet can be modified till it reaches to our machine. An attacker might use techniques such as network eavesdropping to intercept the requests and responses between you and the server and might manipulate the file before you even receive it.

We need a way to verify that the file we've received has not been tempered with.

Subresource Integrity in Browsers

Browsers implement a security feature called Subresource Integrity (SRI) to verify integrity of the resource they fetch and execute.

The garbage looking string of characters you see as the value of integrity attribute is Base64 decoded cryptographic digest formed by applying specific hash algorithm to the contents of the file.

If any characters inside the file changes, then the calculated hash will also change, thus integrity verification will fail, thus the browser will know that the file has been tempered with.

If browsers cannot verify integrity of the file being requested, they'll show error message similar to this:

Failed to find a valid digest in the 'integrity' attribute 
for resource '<resource-name>' with computed SHA-256 integrity '<calculated-hash>'. 
The resource has been blocked.
Enter fullscreen mode Exit fullscreen mode

Performing Integrity Check with NodeJS

To show a little demo how this check might be implemented, I'll use NodeJS's crypto module. We'll use the same jquery file the you saw in the first paragraph. I'll download that file into my machine to be able to make modifications later.

Let's create an index.js file and import fs and crypto modules:

import crypto from 'crypto';
import fs from 'fs';
Enter fullscreen mode Exit fullscreen mode

We'll need fs module to read contents of the file that we want to verify integrity of. Let's declare resource name and expected integrity:

const resource = 'jquery-3.6.4.min.js';
const expectedIntegrity = 'oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=';
Enter fullscreen mode Exit fullscreen mode

Note that sha256 in front of the value of integrity attribute is just the name of hashing algorithm that this hash has been calculated with, so we skip it when comparing.

Let's read the file contents and calculate the digest using sha256 algorithm:

const jquerySource = fs.readFileSync(resource);
const digest = crypto
  .createHash('sha256')
  .update(jquerySource, 'utf8')
  .digest();

const calculatedIntegrity = digest.toString('base64');
Enter fullscreen mode Exit fullscreen mode

The value of digest variable is a type called Buffer which is just bunch of bytes, so we need to convert it to a string in base64 encoding.

And finally, we just need to compare calculatedIntegrity with expectedIntegrity, if they are equal then the jquery-3.6.4.min.js has not been tempered with, if not, it means something has changed inside the file:

if (calculatedIntegrity == expectedIntegrity) {
  console.log('✔️ Resource integrity verified!');
} else {
  console.log(`❌ Failed to find a valid digest in the 'integrity' attribute 
  for resource '${resource}' with computed SHA-256 integrity '${calculatedIntegrity}'.`);
}
Enter fullscreen mode Exit fullscreen mode

If you run this code using node index.js then, you might see this output:

$ node index.js
✔️ Resource integrity verified!
Enter fullscreen mode Exit fullscreen mode

Now, if you change the contents of jquery-3.6.4.min.js file, and run the code again, then you'll see output similar to this:

$ echo 'some malicious code' >> jquery-3.6.4.min.js
$ node index.js
❌ Failed to find a valid digest in the 'integrity' attribute 
  for resource 'jquery-3.6.4.min.js' with computed SHA-256 integrity '+FiqKbj0h12Db1PZj62G+h2BMtOueBg26YQOJFY+6wg='.
Enter fullscreen mode Exit fullscreen mode

Conclusion

I hope this article was helpful for you, I suggest you read more at article at Smashing Magazine about Subresource Integrity and share your thoughts.

You can see the full version of the code we wrote at my GitLab repository.

Top comments (0)