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.
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 dgstcommand without-binaryoutputs 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
.gitattributesto enforce consistent line endings and always hash the final deployed artifact. -
Do not hash gzip-compressed content. CDNs that serve
Content-Encoding: gziporbrdecompress 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.
Related
Permalink to "Related"- Understanding Cryptographic Hash Algorithms for SRI — parent page covering algorithm selection rationale, NIST compliance mapping, and SHA-256 vs SHA-384 vs SHA-512 trade-offs
- Browser Enforcement & Security Boundaries — how the browser fetch pipeline validates the hash, handles CORS opaque responses, and enforces blocking
- Automating Hash Generation in Webpack 5 — build-tool integration so integrity tokens are computed and injected without manual steps
- Configuring Content-Security-Policy with SRI — combining
require-sri-forand hash-based CSP directives for defense in depth