Understanding Cryptographic Hash Algorithms for SRI

Permalink to "Understanding Cryptographic Hash Algorithms for SRI"

This topic is part of Core SRI Fundamentals & Browser Security Boundaries, which covers the full verification pipeline from build-time digest generation through browser-side enforcement. Without a correct understanding of algorithm choice and hash computation, every other SRI control collapses: a weak algorithm is breakable, a miscalculated hash silently blocks assets, and a non-deterministic build produces hashes that drift between environments and deployments.


SRI hash algorithm selection and verification flow Diagram showing a build artifact passing through hash computation (SHA-256, SHA-384, or SHA-512) to produce a base64 digest, which is embedded in an integrity attribute in HTML, then verified by the browser during the fetch lifecycle. Build artifact app.bundle.js Hash computation SHA-256 128-bit SHA-384 192-bit ✓ SHA-512 256-bit → base64-encoded digest integrity attribute integrity="sha384-…" crossorigin="anonymous" embedded in <script>/<link> Browser Fetches resource Recomputes hash ✓ Allow / ✗ Block SHA-384 is the recommended default — highest security/size ratio for production SRI

Prerequisites

Permalink to "Prerequisites"

Before working through algorithm selection and hash generation:


Conceptual Foundation: How SRI Hash Verification Works

Permalink to "Conceptual Foundation: How SRI Hash Verification Works"

SRI is defined in the W3C Subresource Integrity specification. The browser’s fetch lifecycle was extended so that, when an element carries an integrity attribute, the network layer computes a digest over the raw response bytes before passing the resource to the parser or JavaScript engine. If the computed digest does not match the declared value, the fetch is treated as a network error: the resource is not executed, applied, or made available to JavaScript.

Three SHA-2 family algorithms are supported. Each is identified by a case-insensitive prefix in the integrity value:

Algorithm Prefix Security level Base64 token length Recommended use
SHA-256 sha256- 128-bit collision resistance 44 characters Low-risk public assets, legacy tooling constraints
SHA-384 sha384- 192-bit collision resistance 64 characters General production use — industry default
SHA-512 sha512- 256-bit collision resistance 88 characters Compliance frameworks that mandate 256-bit security

SHA-1 and MD5 are explicitly excluded. NIST SP 800-131A deprecated both for all new applications; no browser SRI implementation will ever accept them. The sha384- prefix is the right default for new integrations: it satisfies PCI DSS v4.0.1 requirement 6.4.3, passes SOC 2 Type II evidence review, and produces a token short enough to remain readable in HTML source.

The integrity attribute accepts a space-separated list of hash expressions, allowing you to specify multiple algorithms simultaneously:

<script
  src="https://cdn.example.com/app.bundle.js"
  integrity="sha256-abc123… sha384-xyz789…"
  crossorigin="anonymous">
</script>

The browser selects the strongest algorithm it supports from the list and verifies against that hash. This dual-hash pattern provides forward compatibility: older engines fall back to SHA-256 while modern engines use SHA-384.

The crossorigin="anonymous" attribute is non-negotiable. Without it the browser issues a no-CORS request and receives an opaque response — it cannot read the body bytes to compute the digest. The resource is blocked regardless of whether the hash would have matched.


Step 1 — Compute the Hash Locally

Permalink to "Step 1 — Compute the Hash Locally"

Verify your tooling produces correct output before embedding hashes in HTML. The openssl CLI is the ground-truth reference:

# SHA-384 (recommended)
openssl dgst -sha384 -binary app.bundle.js | openssl base64 -A
# Expected output: a 64-character base64 string, e.g.:
# H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H8aR0YIOHLTDpQ/l2oUzUjMxMroG5LU0

# SHA-256
openssl dgst -sha256 -binary app.bundle.js | openssl base64 -A

# SHA-512
openssl dgst -sha512 -binary app.bundle.js | openssl base64 -A

The base64 output is the value you prepend with sha384- (or the appropriate prefix) and place in the integrity attribute. Run this on the exact bytes the server will serve — after minification, after gzip compression is stripped, after any build-time transforms.

Verification signal: Run the command twice on the same file. If the output differs, your build is non-deterministic. Fix the build pipeline before generating hashes for production.


Step 2 — Automate Hash Generation in the Build Pipeline

Permalink to "Step 2 — Automate Hash Generation in the Build Pipeline"

Manual hash generation does not scale and is error-prone. Automate digest computation as part of the asset build step so the integrity value is always in sync with the file it describes.

Webpack with webpack-subresource-integrity

Permalink to "Webpack with webpack-subresource-integrity"
// webpack.config.js
const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');

module.exports = {
  output: {
    crossOriginLoading: 'anonymous',
  },
  plugins: [
    new SubresourceIntegrityPlugin({
      hashFuncNames: ['sha384'],
    }),
  ],
};

The plugin computes SHA-384 digests for every emitted chunk and injects integrity and crossorigin attributes into the generated HTML. See Automating Hash Generation in Webpack 5 for a complete Webpack 5 configuration including chunk splitting and dynamic import support.

Vite with rollup-plugin-sri

Permalink to "Vite with rollup-plugin-sri"
// vite.config.js
import { defineConfig } from 'vite';
import sri from 'rollup-plugin-sri';

export default defineConfig({
  plugins: [
    sri({ algorithms: ['sha384'] }),
  ],
  build: {
    rollupOptions: {
      output: {
        crossOriginLoading: 'anonymous',
      },
    },
  },
});

CI/CD manifest generation and gating

Permalink to "CI/CD manifest generation and gating"

Emit a JSON manifest of all asset hashes and gate the pipeline on manifest consistency:

# .github/workflows/build.yml
- name: Build assets
  run: npm run build

- name: Generate SRI manifest
  run: |
    node scripts/generate-sri-manifest.js \
      --dir dist \
      --algorithm sha384 \
      --out dist/sri-manifest.json

- name: Gate on manifest baseline
  run: |
    diff dist/sri-manifest.json expected/sri-manifest-baseline.json
    # Fails the step if any hash has changed unexpectedly

Expected output of gating step: Exit 0 (no diff) means all hashes match the committed baseline. Any deviation means either the build is non-deterministic or the source changed — both require human review before deployment.


Step 3 — Embed Integrity Attributes in HTML

Permalink to "Step 3 — Embed Integrity Attributes in HTML"

Once the build pipeline produces digests, embed them in every tag that loads an external resource:

<!-- External script from CDN -->
<script
  src="https://cdn.example.com/vendor/react.production.min.js"
  integrity="sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H8aR0YIOHLTDpQ/l2oUzUjMxMroG5LU0"
  crossorigin="anonymous">
</script>

<!-- External stylesheet from CDN -->
<link
  rel="stylesheet"
  href="https://cdn.example.com/vendor/tailwind.min.css"
  integrity="sha384-PasdfgJnklmpLkjhgMnbjklPLKJHGlkjhGlkjhGlkjhGlkjhGlkjhGlkjhGlkjhG"
  crossorigin="anonymous">

For assets served from the same origin, SRI is optional but still valid: if the origin is compromised, the hashes in the HTML are equally compromised, so same-origin SRI provides defence-in-depth only when combined with a strong Configuring Content-Security-Policy with SRI policy that requires integrity for all scripts.


Step 4 — Understand Browser Enforcement Mechanics

Permalink to "Step 4 — Understand Browser Enforcement Mechanics"

The browser’s fetch enforcement is covered in detail under Browser Enforcement & Security Boundaries. The key points relevant to algorithm selection:

  • The browser normalises the prefix to lowercase and validates the base64 string before making the network request. An invalid base64 value causes an immediate integrity error without a network fetch.
  • When a list of algorithms is declared, the browser’s algorithm priority order is: sha512 > sha384 > sha256. It verifies only the strongest match, ignoring the others.
  • Service workers that intercept fetch events and return cached responses bypass the network-layer integrity check. If you use a service worker, validate integrity inside the fetch event handler before calling respondWith.

Configuration Reference

Permalink to "Configuration Reference"
Option / attribute Valid values Security implication
integrity prefix sha256-, sha384-, sha512- Determines collision resistance; sha384- is the minimum for PCI DSS v4.0.1
Multiple hashes Space-separated list Browser picks strongest; use for algorithm migration without downtime
crossorigin anonymous, use-credentials Must be present; anonymous is correct for public CDN assets
hashFuncNames (webpack) ['sha256'], ['sha384'], ['sha512'], combined Match to the same algorithm used when computing your baseline manifest
algorithms (Vite/Rollup) ['sha256'], ['sha384'], ['sha512'] Same as above

Integration with Adjacent Tooling

Permalink to "Integration with Adjacent Tooling"

Hash computation feeds forward into two downstream controls:

  1. CSP require-sri-for directive — A Content Security Policy can enforce that the browser rejects any script or stylesheet that lacks a valid integrity attribute. This hardens the policy beyond checking hashes: it prevents inline injection of unhashed resources altogether. Configure this via the require-sri-for script style directive alongside your existing CSP headers.

  2. SBOM generation — The hash manifest produced by your build pipeline is the foundation for a software bill of materials. Automated SBOM Generation tools like CycloneDX and SPDX can ingest per-asset SHA-384 hashes and produce compliance-grade artifacts that satisfy SSDF and PCI DSS evidence requirements.

  3. Static asset hash generation — The Static Asset Hash Generation workflow shows how the per-file digests produced here become the source of truth for CDN cache-busting, version manifests, and rollback verification.


Troubleshooting

Permalink to "Troubleshooting"

Failed to find a valid digest in the 'integrity' attribute

Permalink to "Failed to find a valid digest in the 'integrity' attribute"

The base64 string is malformed, the prefix is missing or misspelled, or the hash was computed against a different byte sequence (for example, the pre-minification source). Recompute the hash with openssl dgst -sha384 -binary <file> | openssl base64 -A against the exact file the server will serve and compare character-by-character.

Resource blocked despite correct hash

Permalink to "Resource blocked despite correct hash"

The crossorigin attribute is missing or set to the wrong value. Add crossorigin="anonymous" to the element. Verify in DevTools Network tab that the request includes Origin: and the response includes Access-Control-Allow-Origin:. Without CORS headers on the server response, the browser cannot perform SRI validation even with a correct hash.

Hash drift in CI — same source, different digest

Permalink to "Hash drift in CI — same source, different digest"

Your build is non-deterministic. Common causes: embedded timestamps, random CSS class names (CSS modules without a stable hash seed), different versions of a minifier between CI runners. Pin all toolchain versions in a lockfile and set a deterministic seed for CSS modules. See How to Calculate SHA-256 vs SHA-384 for SRI for a diagnostic checklist.

SRI failure during rolling deployment

Permalink to "SRI failure during rolling deployment"

CDN edge nodes may cache the old asset bytes while the HTML has already been updated to reference the new hash. Use content-addressed filenames (include the content hash in the filename, e.g. app.a1b2c3d4.bundle.js) so the old and new files coexist on the CDN during the rollout window. Alternatively, trigger a CDN cache purge for affected paths before updating the HTML.

integrity attribute ignored on same-origin resources in Firefox

Permalink to "integrity attribute ignored on same-origin resources in Firefox"

This is expected browser behaviour when the crossorigin attribute is absent on a same-origin request. Firefox only enforces SRI on same-origin resources when crossorigin is explicitly set. Add crossorigin="anonymous" even for same-origin assets if you want consistent enforcement across all engines.

SHA-256 hash failing PCI DSS scan

Permalink to "SHA-256 hash failing PCI DSS scan"

PCI DSS v4.0.1 requirement 6.4.3 references NIST approved algorithms. Some QSA tooling flags SHA-256 as insufficient depending on the asset risk classification and key-usage context. Migrate hashFuncNames to ['sha384'] in your build config, regenerate the manifest, and update all integrity attributes. The 64-character base64 token is the only visible change.


Security Boundary

Permalink to "Security Boundary"

Hash algorithm selection and correct SRI configuration protects the integrity of individual resource bytes at the moment of browser delivery. It does not protect against:

  • A compromised build pipeline that injects malicious code before the hash is computed. The hash is computed over the malicious artifact, which then passes browser verification. Pair SRI with Provenance Verification Workflows and signed build attestations (SLSA provenance) to address this threat.
  • Availability attacks — a CDN withholding a resource causes a network error, not an integrity failure. Provide fallback loading with onerror handlers as described in Graceful Fallback Strategies.
  • Data exfiltration via permitted scripts. SRI verifies the bytes of a script are unchanged; it says nothing about what a script does at runtime. Use CSP, Trusted Types, and network egress controls for runtime containment.
  • Server-side rendering injection. SRI only applies to subresources loaded via <script>, <link>, <img>, <audio>, <video>, and <track> elements — not to the HTML document itself.

Frequently Asked Questions

Permalink to "Frequently Asked Questions"
Which hash algorithm should I use — SHA-256, SHA-384, or SHA-512?

SHA-384 is the recommended default for production SRI. It provides 192-bit collision resistance, satisfies PCI DSS v4.0.1 requirement 6.4.3, passes NIST SP 800-131A compliance checks, and is supported by every browser that implements SRI. Use SHA-512 only when a compliance framework explicitly mandates 256-bit security. Use SHA-256 only for low-risk public assets or where token length is a documented constraint.

Can I provide multiple integrity hashes in one attribute?

Yes. The integrity attribute accepts a space-separated list: integrity="sha256-abc… sha384-xyz…". The browser selects the strongest algorithm it supports and verifies against that hash. This lets you migrate from SHA-256 to SHA-384 without a hard cutover — ship both hashes and remove the SHA-256 entry once traffic has migrated to modern engines.

Why does SRI require `crossorigin="anonymous"`?

The browser can only verify a hash if it can read the raw response bytes. Reads are only permitted for CORS-transparent responses, which require an Origin: request header and a valid Access-Control-Allow-Origin: response header. Without crossorigin="anonymous", the browser issues a no-CORS request, receives an opaque response, and cannot perform hash verification — so it blocks the resource as a precaution.

Does SRI protect against a CDN being fully compromised?

Yes, for integrity — if the CDN serves tampered bytes, the browser-computed hash will not match the declared integrity value and the resource will be blocked before execution. SRI does not protect against a CDN withholding the resource (availability) or against a compromised build system that supplied the malicious bytes before the hash was computed.

Will SRI break during a rolling deployment?

It can, if CDN edge caches serve the old asset bytes while the HTML already references the new hash. The fix is content-addressed filenames: include the content hash in the filename so old and new versions coexist during the rollout window. Alternatively, invalidate CDN caches for the affected paths before deploying the new HTML. See the Troubleshooting section above for the full procedure.


Permalink to "Related"

Articles in This Topic

How to Calculate SHA-256 vs SHA-384 for SRI
SHA-256 vs SHA-384 vs SHA-512 for SRI
Back to Core SRI Fundamentals & Browser Security Boundaries