DEV Community

Meriç Cintosun
Meriç Cintosun

Posted on • Originally published at mericcintosun.com

Supply Chain Security in Next.js JavaScript Files with Subresource Integrity

Understanding Subresource Integrity and Its Security Role

Subresource Integrity (SRI) is a mechanism that allows browsers to verify that fetched resources have not been altered in transit or on a compromised server. The browser maintains a cryptographic hash of the expected resource and compares it to the resource it receives before executing any code. If the hashes do not match, the browser refuses to load the resource entirely. This prevents a critical attack vector in modern web applications: compromise of third-party CDNs, package registries, or even your own origin server.

The threat model here is precise. An attacker who gains control over the CDN serving your JavaScript files can inject arbitrary code into those files. Without SRI, users' browsers would execute this malicious code with full application privileges. SRI breaks this chain by making it cryptographically impossible for an attacker to modify the resource without the publisher knowing about it and issuing a new signed hash.

Next.js applications inherently rely on serving JavaScript bundles from a CDN or origin server. Every page load, every client-side navigation, and every dynamic import pulls JavaScript from the network. The larger and more distributed your application, the more servers and intermediaries touch your code before it reaches a user's browser. SRI gives you a mechanism to guarantee end-to-end integrity from source to execution.

The Mechanics of SRI: Hash Algorithms and Implementation

SRI relies on cryptographic hashing to work. The most commonly supported algorithms are SHA-256, SHA-384, and SHA-512. When you generate an SRI hash, you compute the hash of the file content, encode it in base64, and prefix it with the algorithm identifier. A complete SRI hash looks like this:

sha384-abcdefg1234567890=
Enter fullscreen mode Exit fullscreen mode

The format is algorithm-base64encodedHash. The browser extracts the algorithm from this string and computes the same hash over the downloaded resource. If the computed value matches the provided value, the resource loads. If it does not match, the resource is rejected and a security error is raised in the console.

Multiple hash algorithms can be specified in a single integrity attribute as a space-separated list. The browser uses the first algorithm it recognizes, so you might write:

<script
  src="https://cdn.example.com/app-bundle.js"
  integrity="sha384-ABC... sha256-DEF..."
></script>
Enter fullscreen mode Exit fullscreen mode

This provides compatibility with older browsers that might not support SHA-384 while still offering stronger hashing on modern browsers. The browser will use SHA-384 if available; otherwise it falls back to SHA-256.

Computing these hashes is not a manual process. Build tools and package managers automate this. When Next.js builds your application with SRI support enabled, it computes the hash of each output bundle and embeds the hash into the HTML that references it. The development workflow becomes seamless: build, hash, embed, deploy.

Next.js 16.2 and Turbopack Integration

Next.js 16.2 introduced native SRI support through improvements to its build system. The Turbopack bundler, which powers Next.js builds in recent versions, gained the ability to compute SRI hashes for all output bundles and automatically insert the integrity attributes into the HTML served to clients.

This integration works by default when you build a Next.js application for production. The build process analyzes each JavaScript bundle, computes its SHA-384 hash (or SHA-256 depending on configuration), and writes the integrity attribute directly into the <script> tags in your HTML output. No manual configuration is required for basic functionality, though fine-grained control is available for advanced use cases.

The Turbopack bundler is significantly faster than Webpack, which is the historical bundler in Next.js applications. Faster builds mean you can iterate on security configurations more easily and regenerate SRI hashes as part of your normal deployment pipeline. The performance improvement is not merely a convenience; it enables more aggressive security practices. If your build takes two hours, you are unlikely to regenerate SRI hashes on every change. If your build takes two minutes, regenerating hashes becomes routine.

To verify that SRI is being applied to your bundles, inspect the HTML output after building. The <script> tags should contain integrity="sha384-..." attributes. You can check this with a simple curl or by opening your deployed page and viewing the page source in your browser's developer tools.

Threat Model: Third-Party CDN Compromise

The concrete threat that SRI defends against is compromise of a third-party content delivery network. Many applications serve JavaScript from CDNs operated by companies other than themselves: Cloudflare, AWS CloudFront, Azure CDN, or others. These CDNs are high-value targets because they serve content for millions of websites. A breach that goes undetected for even a few hours can affect millions of users across thousands of applications.

In October 2023, the cdn.jsdelivr.net service experienced a partial outage, but more importantly it demonstrated the scale of exposure: a single CDN serves JavaScript to a significant portion of the web. If an attacker gained write access to this CDN, they could modify any file served through it. Users would download and execute the modified code without any warning or verification.

SRI does not prevent the compromise of the CDN itself. Attackers can still break into servers and modify files. What SRI does is make the compromise detectable and unusable from the attacker's perspective. If the attacker modifies a file that has an SRI hash in the HTML, users' browsers will compute a different hash, the verification will fail, and the browser will refuse to load the resource. The application breaks, which triggers alerts and investigation. The attacker gains nothing because the malicious code never executes.

This asymmetry is the core value proposition of SRI. The attacker must either avoid triggering the integrity failure (which means they cannot actually modify the code), or they trigger it immediately and compromise is detected in real time. There is no silent middle ground where malicious code executes undetected.

Generating and Embedding SRI Hashes in Your Build Process

When using Next.js 16.2 or later with Turbopack, SRI hash generation is built into the build pipeline. However, understanding the process helps you debug issues and ensure correctness.

The build process proceeds in stages. First, Turbopack analyzes your application code, resolves dependencies, and creates bundling plan. It then executes the bundling: concatenating code, applying transformations, and generating the final .js files that users download. At this point, Turbopack computes a cryptographic hash over each bundle file. It encodes this hash in base64 and creates the integrity attribute.

Turbopack then generates the HTML that will be served to users. For each <script> tag that references a JavaScript bundle, Turbopack inserts the integrity attribute alongside the src. The HTML is then written to disk and becomes part of your deployment artifact.

The integrity attribute can be applied not only to your own bundles but also to external scripts that you load. If your Next.js application dynamically loads a third-party script, you can compute the SRI hash for that script and embed it in your code. For example:

const script = document.createElement('script');
script.src = 'https://api.example.com/analytics.js';
script.integrity = 'sha384-ABC123...';
script.crossOrigin = 'anonymous';
document.head.appendChild(script);
Enter fullscreen mode Exit fullscreen mode

The crossOrigin attribute must be set to anonymous for SRI to work with cross-origin resources. This tells the browser to request the resource without sending cookies or authentication headers, which is necessary for CORS reasons.

Computing the hash for external third-party scripts requires that you either download the script and compute the hash locally, or that the third-party provider publishes the SRI hash for you. Many popular CDNs and service providers now publish SRI hashes in their documentation. If they do not, you must assume responsibility for obtaining the hash through a secure channel.

Configuration and Customization

Next.js applications can customize SRI behavior through the next.config.js file. The configuration options allow you to enable or disable SRI, specify which hashing algorithm to use, and control which resources receive integrity attributes.

A minimal configuration that explicitly enables SRI looks like this:

// next.config.js
module.exports = {
  experimental: {
    sri: {
      enabled: true,
      algorithm: 'sha384',
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

The algorithm option accepts sha256, sha384, or sha512. SHA-384 is recommended as a balance between security strength and hash size. SHA-512 produces longer hashes that marginally increase HTML file size; SHA-256 is weaker than SHA-384 and should only be used if you need compatibility with very old browsers.

You can also exclude certain bundles from SRI protection if needed, though this should be rare. Typically you might exclude inline scripts or bundles that change frequently during development. The configuration option for this varies depending on your Next.js version, so consult the official Next.js documentation for your specific version.

Once SRI is enabled, it applies automatically to all output bundles. You do not need to manually insert integrity attributes or modify your component code. The build system handles everything. When you run next build, the output HTML files will contain all necessary integrity attributes.

Validating SRI Implementation in Production

After deploying a Next.js application with SRI enabled, verify that the integrity attributes are present and correct. The simplest check is to fetch the HTML and search for integrity= in the response.

curl https://yourapp.example.com | grep -i integrity
Enter fullscreen mode Exit fullscreen mode

This should return one or more lines showing script tags with integrity attributes. If the command returns nothing, SRI is not being applied, which indicates a configuration error.

A more thorough validation involves computing the hash of downloaded bundles yourself and comparing them to the integrity attributes. For each script tag with an integrity attribute, extract the hash, download the bundle, and compute its hash using the same algorithm.

# Download the bundle
curl -s https://cdn.example.com/bundle-abc123.js > bundle.js

# Compute SHA-384 hash
sha384sum bundle.js

# Compare to the integrity attribute from the HTML
# If they match (after base64 encoding), SRI is working correctly
Enter fullscreen mode Exit fullscreen mode

The hash you compute will be in hexadecimal format, but the SRI hash in the HTML is base64-encoded. You need to convert between formats or use a tool that handles the encoding automatically. Many online SRI hash generators can verify this for you, though in a production environment you should automate this check as part of your deployment validation pipeline.

Beyond manual verification, monitor your application's error logs and browser console for SRI failures. When an integrity check fails, the browser logs a clear error message. If you see these errors, it indicates that a bundle was modified during transit or on the server, which requires immediate investigation.

Key Deployment Considerations

When deploying a Next.js application with SRI enabled, several operational practices become important.

First, ensure that your build process is deterministic. If the same source code produces different bundles each time you build, the hashes will change unpredictably and SRI verification will fail. Use the same build environment, Node.js version, and dependency versions for every build. Next.js and Turbopack are deterministic by design, but your custom build scripts or build plugins might not be.

Second, coordinate your bundle deployment with HTML deployment. The integrity hashes are embedded in the HTML. If you deploy new bundles before deploying the new HTML, users will request bundles that do not exist, and their browsers will reject the old bundles due to hash mismatch. Both must be deployed as an atomic unit. Most deployment systems handle this naturally (you deploy a single artifact containing both HTML and bundles), but if your deployment process separates them, take care to update them together.

Third, establish a process for updating external third-party scripts. If you load a third-party analytics script or widget with an SRI hash, and the third-party provider updates their script, your embedded hash becomes stale. The new script will have a different hash and your application will reject it. Subscribe to security bulletins and changelogs from third-party providers, and establish a process to update these hashes regularly. Some providers make this easier by publishing hashes in their documentation or by providing an API to query the current hash.

Fourth, maintain a comprehensive inventory of all SRI hashes in your application, including those for external scripts. Document which scripts are protected, which algorithm is used, and where the hash came from (vendor documentation, computed locally, etc.). This inventory becomes a reference when debugging SRI failures and when updating hashes.

Real-World Limitations and Complementary Measures

SRI is powerful but operates within bounds. It cannot protect against compromises that occur before the bundle is signed. If your build server is compromised, an attacker can insert malicious code into your bundles and recompute the SRI hashes as part of the same attack. SRI protects against compromises of intermediaries (CDNs, caches, etc.) that do not have access to your private build infrastructure.

SRI also cannot protect against compromises of your origin server if the attacker has the ability to update the HTML itself. If an attacker gains control of your web server and can modify both the JavaScript bundles and the HTML that references them, they can update both the integrity hashes and the bundles in lockstep, defeating SRI. However, this same level of compromise would allow them to inject malicious code without SRI anyway, so SRI does not worsen your security posture in this scenario.

For comprehensive supply chain security, SRI must be combined with other practices. Code signing and verification of your build artifacts ensures that only authorized builds enter your deployment pipeline. Secure build infrastructure with restricted access and audit logging ensures that compromises of the build process are detected. Content Security Policy (CSP) headers restrict where scripts can be loaded from and what permissions they receive, providing a defense in depth even if SRI fails.

A mature security posture combines all three: SRI for integrity verification of downloaded resources, CSP for runtime execution restrictions, and secure build practices to prevent compromise at the source. SRI addresses the supply chain between your infrastructure and users. The other measures address the supply chain before deployment and the runtime behavior after loading.


For professional Web3 documentation or full-stack Next.js development work on your projects, visit https://fiverr.com/meric_cintosun.

Top comments (0)