Introducing experimental integrity policies to Node.js | Snyk


A recent experimental feature for introducing integrity policies landed in Node.js core 11.8.0. This capability, shipped in non LTS version yet, provides integrity checks for a Node.js runtime when modules are being loaded, in order to verify that the modules code haven’t been tampered with.

Bradley Farias introduced this change in October 2018 and borrowed the idea from a similar security feature that exists for browsers – Subresource Integrity.

Subresource integrity

The World Wide Web Consortium (W3C) proposed a new integrity field to the HTML spec that will enable verification of subresources in an HTML document. Web pages can instruct the browser to load resources through a script or a link tag, however, unless an integrity field is used for such HTML elements, there is no way to verify whether a resource has been tampered with.

Web pages load their own or third-party resources from a remote server, most commonly is the utilization of a Content Delivery Network (CDN) for doing that. When loading such resources, verifying the integrity of these resource files is helpful to protect against malicious actors. Examples of this include:

  • A compromised remote server that may serve modified copies of the resource file
  • A compromised transport medium and its resource file

Integrity policies for Node.js modules

Inspired by the browser’s security mechanism to enforce resource integrity – what if Node.js was able to enforce the same for third-party modules as they are required by program code and check them before they are loaded into memory and executed?

The new and experimental policies feature in Node.js 11.8.0 allows us to enforce this behavior through the command line flag –experimental-policy which expects a policy manifest to be provided for all the resources in a project. Let’s see how this works!

A Node.js policy manifest

The 11.8.0 release only provides the module loader enforcement, but Node.js is currently not capable of handling the policy manifest. To manage the policy file we can turn to Bradley’s own node-policy CLI helper:

 npm install -g @bradleymeck/node-policy 

By invoking node-policy’s integrity:add command we can create the policy manifest that contains all the cryptographic hashes created for all files in the current directory. This includes everything from the .git/ directory, to all the contents of node_modules/, so you can expect a small amount of time for everything to be generated.

To demonstrate how this works, we will use it on a CLI Node.js module that I created to help me with exporting typeform survey results.

Invoke node-policy so that it creates an integrity manifest file for all files in the current directory (noted by the last dot argument), and specify an algorithm to use as the cryptographic hash function. The available options to choose from are sha256, sha384, and sha512, which correspond with the Subresource Integrity HTML spec.

Node.js integrity policies sha checksum modules

The generated policy file for the project is saved in the user’s home directory. Here’s a small snippet from .node-policy.json where we can see how the structure of a manifest file looks like, having the file URLs and their integrity hash set:

 "resources": { "./projects/repos/typeform-export-excel/.git/COMMIT_EDITMSG": { "integrity": "sha256-dMEh66Uy32Nr8wO5fy3FAEsZjTeqpBghe9o48gYgZfM=" }, "./projects/repos/typeform-export-excel/.git/FETCH_HEAD": { "integrity": "sha256-NsU93Hbe+fB2KxZIzQnA299sQRl/4Fg1wk9M0FSYe7E=" }, "./projects/repos/typeform-export-excel/node_modules/zip-stream/index.js": { "integrity": "sha256-JUKnwX/rS+DRZ0tEQjTl4B/qb3/z7DRMqYiPy9EJpOg=" }, "./projects/repos/typeform-export-excel/package.json": { "integrity": "sha256-8YN17NVVfPPW+CWsQ2j5LF9MbcnTu6ny/MFkVj/pXjI=" }, "./projects/repos/typeform-export-excel/src/constants.js": { "integrity": "sha256-wGKtyPIeYuAc0D5L7SrPeiXOqvZYNgqJLkxi8aA/OiQ=" }, "./projects/repos/typeform-export-excel/src/index.js": { "integrity": "sha256-eJnVeGBKu6vVMHPVS2PKpK6/adC6YLiAEyoIqQwwOjI=" } 

If we were to run the node-policy CLI on other project directories it would append more files to this policy manifest. This is node-policy’s implementation and not a spec or guideline.

Why does node-policy create the policy file in the user’s home directory? While we have yet to standardize comprehensive best practices for the integrity policy, due to the file’s nature of being the source of truth for trusted resources it is in our best benefit to protect the file from somehow being manipulated. A couple of best practices to follow is to set that file is read-only, and locate it outside any potentially accessible directory.

To simulate tampering with a file and how Node.js will catch this issue I made a small code update to bin/cli.js which is part of the CLI project. When invoking Node.js to run the executable CLI JavaScript code it detects that the hash is different, before loading this CommonJS module, and throwing an error that drops Node.js back to the command prompt.

Node.js integrity policies CLI for node modules

The current implementation allows logging of integrity mismatches instead of throwing an error, or exiting the program. This is made possible with an onerror property that can be added to the top level object of the policy manifest file.

Summary

The continuous work on building security features into Node.js is promising and we will surely see more of it in the near future. Keep in mind that the integrity policy feature is still experimental and also only available from Node.js 11.8.0.