How to Calculate SHA-256 vs SHA-384 for SRI

Permalink to "How to Calculate SHA-256 vs SHA-384 for SRI"

Part of Understanding Cryptographic Hash Algorithms for SRI, this page covers exactly how to produce a valid integrity attribute value — from raw file to formatted hash token — using the command line, Node.js, Python, and automated build pipelines.

Quick Reference

Permalink to "Quick Reference"
Property SHA-256 SHA-384
Algorithm identifier sha256 sha384
Collision resistance 128-bit 192-bit
Base64 digest length 44 characters 64 characters
NIST SP 800-131A Approved Approved
PCI DSS v4.0.1 Acceptable Recommended
Browser support All major engines All major engines
Recommended for SRI Baseline workloads Default choice

Use SHA-384 unless a specific constraint forces SHA-256. The longer token is a negligible overhead compared with the security margin it adds.

Why SRI Hashing Works the Way It Does

Permalink to "Why SRI Hashing Works the Way It Does"

A browser that fetches https://cdn.example.com/lib.min.js cannot know whether the CDN has served the authentic file or a version silently modified by a supply chain attacker. SRI solves this by letting you embed a cryptographic digest of the expected file directly in your HTML. The browser computes the same digest over the downloaded bytes and blocks execution if the two values diverge. This verification runs client-side, post-download, pre-parse — after TLS terminates but before any JavaScript runs.

The digest must be computed over the exact bytes the server transmits, not over a local build artifact that may differ by whitespace, line endings, or minifier version. This is the single most common source of SRI breakage in production.

The broader enforcement mechanics — including how the fetch pipeline handles opaque responses and service-worker interception — are covered in Browser Enforcement & Security Boundaries.


SRI Hash Computation Flow Diagram showing the path from CDN asset bytes through SHA-384 hashing to base64 encoding and finally the browser integrity attribute comparison that results in either allow or block. CDN Asset (bytes) SHA-384 digest Base64 encode Browser compares to integrity="" Allow ✓ Block ✗ match mismatch

Canonical Implementation

Permalink to "Canonical Implementation"

The following example is a complete, production-ready <script> tag with a SHA-384 integrity attribute. Copy this pattern for every cross-origin script reference.

<script
  src="https://cdn.example.com/lib.min.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"></script>

To generate the sha384-… token for your own file:

# OpenSSL — works on macOS, Linux, Windows (Git Bash / WSL)
openssl dgst -sha384 -binary lib.min.js | openssl base64 -A

The -binary flag outputs raw bytes instead of a hex string. The -A flag suppresses newline wrapping. Omitting either flag produces a value the browser will reject.

Variant Examples

Permalink to "Variant Examples"

SHA-256 via OpenSSL

Permalink to "SHA-256 via OpenSSL"

Use this when an older compliance regime explicitly limits you to SHA-256, or when you need the shorter token for size-constrained environments:

openssl dgst -sha256 -binary lib.min.js | openssl base64 -A
# Produces a 44-character base64 string
# Prefix with sha256- before placing in the integrity attribute

Node.js Crypto Module

Permalink to "Node.js Crypto Module"

Integrate into build scripts or CI verification utilities:

import { createHash } from 'crypto';
import { readFileSync } from 'fs';

function sriHash(filePath, algorithm = 'sha384') {
  const content = readFileSync(filePath);
  const digest = createHash(algorithm).update(content).digest('base64');
  return `${algorithm}-${digest}`;
}

// Usage
console.log(sriHash('dist/lib.min.js'));
// → sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/...

Multi-algorithm token for graceful migration

Permalink to "Multi-algorithm token for graceful migration"

Space-separate tokens to let the browser pick the strongest algorithm it supports. This pattern enables a rolling algorithm upgrade without a hard cut-over:

<script
  src="https://cdn.example.com/lib.min.js"
  integrity="sha256-abc123def456…== sha384-oqVuAfXRKap7fdgcCY5uykM6…"
  crossorigin="anonymous"></script>

The browser selects only the strongest supported algorithm from the list and verifies against that digest alone.

Python 3

Permalink to "Python 3"

Useful in deployment scripts or pre-push hooks outside Node.js environments:

import hashlib, base64, sys

def sri_hash(path: str, algorithm: str = "sha384") -> str:
    h = hashlib.new(algorithm)
    with open(path, "rb") as f:
        h.update(f.read())
    digest = base64.b64encode(h.digest()).decode()
    return f"{algorithm}-{digest}"

print(sri_hash(sys.argv[1]))

Webpack 5 automatic injection

Permalink to "Webpack 5 automatic injection"

For build pipelines where manual hash management is impractical, the webpack-subresource-integrity plugin computes and injects integrity attributes at bundle time. Full configuration detail is in Automating Hash Generation in Webpack 5.

// webpack.config.js
import { SubresourceIntegrityPlugin } from 'webpack-subresource-integrity';

export default {
  output: {
    crossOriginLoading: 'anonymous',
  },
  plugins: [
    new SubresourceIntegrityPlugin({ hashFuncNames: ['sha384'] }),
  ],
};

Gotchas and Edge Cases

Permalink to "Gotchas and Edge Cases"
  • Hash the served artifact, not the source file. Minifiers, transpilers, and bundlers transform your source. The integrity hash must match the exact bytes the CDN delivers. Calculate after your full build pipeline completes, not before.

  • Always add crossorigin="anonymous" on cross-origin tags. Without it the browser issues a no-CORS request and receives an opaque response whose body it cannot read for comparison. The resource is blocked immediately, not because the hash failed but because the body was never available. This is the most common real-world SRI bug.

  • Never encode text — always pipe raw binary. The openssl dgst command without -binary outputs a hex string. Base64-encoding hex characters instead of raw bytes produces a completely different value the browser will never accept.

  • Line-ending differences invalidate the hash. A file normalized to LF on Linux produces a different digest than the same file with CRLF on Windows. Configure .gitattributes to enforce consistent line endings and always hash the final deployed artifact.

  • Do not hash gzip-compressed content. CDNs that serve Content-Encoding: gzip or br decompress before delivery. The browser hashes the decompressed bytes. Hash your uncompressed artifact and let the CDN handle transfer encoding transparently.

  • Service workers can bypass SRI. A service worker that intercepts the fetch and returns a cached response may circumvent the hash check. If your application uses service workers with caching, review the interaction with Browser Enforcement & Security Boundaries before deploying.

Verification Steps

Permalink to "Verification Steps"

1. Confirm the token format in the HTML

Permalink to "1. Confirm the token format in the HTML"

Open the built HTML file and check that every cross-origin <script> or <link> carries both integrity and crossorigin:

grep -E 'integrity=|crossorigin=' dist/index.html

Expected output for each tag (tokens will differ):

integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" crossorigin="anonymous"

2. Cross-check the hash against the live CDN asset

Permalink to "2. Cross-check the hash against the live CDN asset"

Download the file the CDN actually serves and re-hash it:

curl -sL https://cdn.example.com/lib.min.js | openssl dgst -sha384 -binary | openssl base64 -A

The output must match the token in your HTML character-for-character.

3. Confirm clean browser validation

Permalink to "3. Confirm clean browser validation"

Load the page in Chrome or Firefox with DevTools open. An SRI failure produces a console error in this exact format:

Failed to find a valid digest in the 'integrity' attribute for resource
'https://cdn.example.com/lib.min.js' with computed SHA-384 integrity
'sha384-xyz…'. The resource has been blocked.

If no such message appears and the script executes, the hash is correct. The Network tab will show a 200 OK for the resource.

4. CI gate — fail on hash drift

Permalink to "4. CI gate — fail on hash drift"

Add a verification step to your pipeline that re-derives all hashes and compares them to the values already in the built HTML. For Static Asset Hash Generation pipelines this is typically a JSON manifest comparison:

# GitHub Actions
- name: Verify SRI integrity tokens
  run: |
    node scripts/verify-sri.js --manifest dist/sri-manifest.json --dir dist/

The script should exit with a non-zero code on any mismatch, blocking the deployment before stale or tampered hashes reach a CDN edge node.


Permalink to "Related"

Related Articles

SHA-256 vs SHA-384 vs SHA-512 for SRI
Understanding Cryptographic Hash Algorithms Core SRI Fundamentals & Browse…