RedScore.ai

Fixes

Third-Party Risk Surface · Updated 2026-05-02

Subresource Integrity (SRI)

Tamper-detection hashes on external scripts. All SRI-covered: pass. >=50%: 0.6. <50%: 0.28. None: 0.0.

Subresource Integrity (SRI, W3C standard) adds tamper-detection hashes to third-party scripts and stylesheets. The browser fetches the resource, hashes it, compares to the declared hash, and refuses to execute if they do not match. Without SRI, every external script is a supply-chain dependency: if the CDN serving it is compromised, or the third-party project pushes malicious code, your site executes attacker code under your origin's trust. SRI is the cheap, browser-native defense.

How the check works

Per primary host, the check parses the response HTML and inspects every external <script> tag. Dynamically-served scripts (Google Tag Manager, Stripe.js, Mapbox, and similar where the content changes per request and SRI is fundamentally incompatible) are excluded. The remaining external scripts are evaluated for an integrity attribute. Per-host scores:

  • No external scripts after exclusions: 1.0 (nothing to protect).
  • All evaluated scripts have SRI: 1.0 (full credit).
  • At least 50% have SRI: 0.6 (partial). Reason: partial_sri.
  • Under 50% have SRI: 0.28 (minimal). Reason: minimal_sri.
  • None have SRI: 0.0. Reason: no_sri.

Per-host scores aggregate via weighted average across primary hosts. Verdict via the standard tech composite mapping.

How the verdict maps to evidence

  • Pass: every evaluated script has integrity, or no eligible scripts exist on the page.
  • Warn: 50% or more eligible scripts have SRI (partial coverage).
  • Fail: under 50% have SRI, or none do.

Evidence shows sri_counts (with_sri / total / total_scripts_observed / excluded_dynamic), domains_without_sri (which third-party hosts are unprotected), and excluded_script_hosts (which were skipped as dynamic).

Special states

  • Not Applicable: primary host serves a default/empty placeholder, or response is not HTML.
  • Degraded: probe data unavailable. Fix Web Assessability first.

Fix: add integrity attributes to every static external script

Adding SRI is a one-time edit per script tag. Once in place, it does not need maintenance unless the underlying script changes (you would re-pin to the new version anyway).

1. Pin to a specific version URL

SRI hashes are content-based. If the URL serves "latest" (which can change), the hash will go stale and the browser will refuse to load it. Pin every external script to a specific version. Most CDNs serve versioned URLs:

Pin to a specific version (good)

<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"
        integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"
        crossorigin="anonymous"></script>

<!-- unpkg -->
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"
        integrity="sha384-..."
        crossorigin="anonymous"></script>

Latest URL (bad: hash will stale)

<!-- DO NOT do this with SRI: -->
<script src="https://cdn.example.com/lib/latest/script.js"
        integrity="sha384-..."></script>

<!-- The hash matches today's content; tomorrow's "latest" may differ
     and the browser will refuse to load. -->

2. Generate the integrity hash

SHA-384 is the most common (specs allow SHA-256, SHA-384, SHA-512; 384 is the sweet spot of size and broad support). Three ways:

Web tool (one-off)

https://www.srihash.org/

Paste the URL of the script; it returns the integrity attribute string.

openssl (CLI, scriptable)

# Fetch and hash:
curl -s https://cdn.example.com/lib/1.2.3/script.js \
  | openssl dgst -sha384 -binary \
  | openssl base64 -A

# Output: <hash>
# Use as: integrity="sha384-<hash>"

Node.js (scriptable in build)

import crypto from "crypto";
import fs from "fs";

const content = fs.readFileSync("vendor.js");
const hash = crypto.createHash("sha384").update(content).digest("base64");
console.log(`integrity="sha384-${hash}"`);

3. Add the crossorigin attribute

Cross-origin SRI requires CORS to work. Without crossorigin="anonymous" on the tag, the browser will not validate SRI for cross-origin resources. The CDN must also send Access-Control-Allow-Origin headers (most major CDNs do by default for static assets):

Full SRI script tag

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"
        integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"
        crossorigin="anonymous"
        referrerpolicy="no-referrer"></script>

4. Automate via build tooling

Manual hash maintenance breaks at scale. Use build tooling that generates SRI hashes automatically when bundling:

  • Webpack: webpack-subresource-integrity plugin. Generates SRI for bundled chunks at build time.
  • Vite: vite-plugin-html with sri or @small-tech/vite-plugin-sri.
  • Rollup: @rollup/plugin-sri.
  • Hugo: srcset and integrity helpers in templates.
  • Jekyll: jekyll-sri plugin auto-generates integrity for assets.

Stylesheets too (link rel="stylesheet")

SRI also works on <link rel="stylesheet">. The same browser-side validation runs; missing-or-mismatched integrity blocks the stylesheet. Same syntax as scripts:

Stylesheet with SRI

<link rel="stylesheet"
      href="https://cdn.example.com/lib/1.2.3/style.css"
      integrity="sha384-..."
      crossorigin="anonymous">

Verify the fix

  • View page source. Every external <script src="..."> should have integrity= and crossorigin= attributes (except dynamically-served scripts like Google Tag Manager).
  • Open the page in DevTools → Network. SRI failures show up as blocked requests with a "Failed to find a valid digest in the integrity attribute" message.
  • Test a deliberately-broken hash (change one character) on a non-production page; the browser should refuse to load and log an SRI error to the console.
  • Re-run the RedScore lookup. SRI coverage updates immediately on the next scan.

Common pitfalls

  • Pinning to "latest" URL with SRI. The hash matches today, fails tomorrow when the CDN updates. Always pin to a specific version.
  • Forgetting crossorigin attribute. Without it, browsers do not validate SRI on cross-origin resources. Add crossorigin="anonymous" alongside every integrity attribute.
  • CDN does not send CORS headers. SRI validation needs the response to be CORS-eligible. Major CDNs (jsDelivr, unpkg, cdnjs) handle this; some custom CDNs do not. Test with crossorigin="anonymous" and watch DevTools for CORS errors.
  • Dynamically-served scripts. Google Tag Manager, Stripe Elements, Mapbox, Recaptcha. SRI is incompatible by design (different content per request). The check excludes these from scoring; do not try to add SRI to them, you will break them.
  • Self-hosted scripts on your own origin. Same-origin scripts are already trusted via same-origin policy and do not strictly need SRI; the check focuses on third-party (cross-origin) scripts. Adding SRI to first-party scripts is not harmful but provides no additional security against same-origin compromise.
  • Forgetting to update SRI when upgrading library version. Upgrading from jquery@3.7.0 to jquery@3.7.1 changes the hash; if you update the URL but not the integrity, the browser refuses to load. Automate via build tooling.
  • Treating SRI as XSS defense. SRI prevents malicious modification of trusted external resources. It does NOT protect against malicious code injected into your own page (XSS), use CSP for that. SRI and CSP are complementary, not redundant.

What to do next

See how these recommendations apply to your site's current scan results.

Scan domain