Supply Chain Auditing & Dependency Verification
Permalink to "Supply Chain Auditing & Dependency Verification"Modern applications ingest hundreds of external packages and CDN-hosted assets per deployment, making the software supply chain the widest-surface attack vector in production security. This section covers the full cryptographic assurance stack — from lockfile integrity and dependency pinning through SBOM generation, provenance attestation, and browser-level SRI enforcement — so that security engineers, DevOps teams, and compliance leads can build deterministic, verifiable delivery pipelines.
1. Supply Chain Threat Landscape & Attack Vectors
Permalink to "1. Supply Chain Threat Landscape & Attack Vectors"Dependency ecosystems face persistent, automated exploitation campaigns that exploit the implicit trust organizations place in upstream registries and CDN providers. Understanding each vector is the prerequisite for mapping controls.
Dependency confusion. Attackers publish malicious packages to public registries (npm, PyPI, RubyGems) using the same names as private internal packages, but with artificially higher semantic version numbers. Package managers that resolve from both public and private registries will pull the higher-versioned public package unless the private registry takes strict precedence.
Typosquatting. Campaigns register packages with names visually similar to widely used libraries (lodahs, momnet, crossenv). Developer fatigue during dependency installation results in mistaken npm install commands resolving to malicious code that executes at post-install hook time.
Maintainer account compromise. Direct injection of backdoors into widely adopted packages requires only access to a single maintainer account with publish rights. Malicious releases pass registry checksums because the registry signs what was published — it does not validate what the code does.
Build-time script execution. Package postinstall hooks execute arbitrary shell commands during npm install. A compromised transitive dependency three levels deep can exfiltrate CI secrets, modify build artifacts, or inject code into your final bundle.
CDN cache poisoning and supply. Assets hosted on third-party CDNs can be replaced by an attacker who gains write access, by a MitM at the network layer, or through CDN provider misconfiguration. Without Subresource Integrity (SRI), browsers execute the served payload blindly.
Threat models must account for all four stages of the delivery pipeline: registry resolution, build-time execution, artifact delivery at the CDN edge, and runtime loading inside the browser. Controls at any single stage leave the adjacent stages exposed.
2. Core Standards: Lockfiles, SBOMs, and the SRI Specification
Permalink to "2. Core Standards: Lockfiles, SBOMs, and the SRI Specification"Lockfile Integrity
Permalink to "Lockfile Integrity"npm’s package-lock.json, Yarn’s yarn.lock, and pnpm’s pnpm-lock.yaml each record an integrity field per package entry. This SHA-512 (npm) or SHA-1 (legacy Yarn) hash covers the exact tarball bytes published to the registry. npm ci validates these hashes on every install, refusing to proceed if the downloaded tarball hash diverges from the lockfile record.
"node_modules/lodash": {
"version": "4.17.21",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZkE/dvIZAyfVyjriPpg==",
...
}
The lockfile integrity field guards install-time resolution. It does not guard what the browser downloads at runtime — that is the SRI integrity attribute’s job.
Software Bill of Materials (SBOM)
Permalink to "Software Bill of Materials (SBOM)"An SBOM is a machine-readable inventory of every component in a software artifact, including transitive dependencies, version, license, and purl (package URL). The two dominant formats are CycloneDX (JSON/XML, OWASP-maintained) and SPDX (tag-value/JSON, Linux Foundation). Automated SBOM Generation covers pipeline integration for both formats.
PCI DSS v4.0.1 Requirement 6.3.2 now explicitly requires a software inventory. An SBOM satisfies this requirement when it is generated automatically at build time, signed with a verifiable key, and stored as a versioned artifact alongside the build output.
The W3C SRI Specification
Permalink to "The W3C SRI Specification"The W3C Subresource Integrity Level 1 specification defines the integrity attribute for <script>, <link rel="stylesheet">, and <link rel="preload"> elements. The attribute value is one or more hash tokens of the form <algorithm>-<base64-value>:
<script
src="https://cdn.example.com/app.v3.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous">
</script>
The crossorigin="anonymous" attribute is mandatory on any cross-origin resource tagged with integrity. Without it, the browser performs a no-CORS fetch and receives an opaque response whose bytes are unavailable for hash comparison; the request fails unconditionally.
Algorithm selection rationale:
| Algorithm | Output bits | Collision resistance | Recommended use |
|---|---|---|---|
| SHA-256 | 256 | 128-bit | Acceptable; outdated for new deployments |
| SHA-384 | 384 | 192-bit | Standard for web assets; matches PCI DSS 6.4.3 |
| SHA-512 | 512 | 256-bit | Maximum assurance; 33% larger attribute value |
SHA-384 is the default for all examples on this site, matching industry convention and regulatory guidance.
3. Browser Enforcement Mechanics
Permalink to "3. Browser Enforcement Mechanics"Understanding the fetch lifecycle prevents common misconfigurations and helps teams diagnose mismatch errors correctly.
- Parse. The browser’s HTML parser encounters a
<script integrity="...">tag and queues a fetch. - Fetch. The resource is retrieved. For cross-origin resources, the response must include
Access-Control-Allow-Origin. - Hash computation. The browser computes the digest of the raw response bytes using the algorithm specified in the
integrityattribute. - Comparison. The computed digest is compared against every token listed in
integrity. If any token matches, verification passes. - Execution gate. On match: the resource is executed or applied. On mismatch: the resource is blocked, a
SecurityErroris fired, and a console error is logged. Theonerrorhandler on the element fires.
Failure modes and opaque responses. If a CDN serves the resource without CORS headers, the browser cannot read the response body for hashing, and the SRI check fails regardless of whether the content is correct. This is the most common production SRI breakage point — a sudden CDN policy change that drops CORS headers can silently break all SRI-tagged assets site-wide.
SRI does not validate TLS certificate pinning, pre-fetch origin authenticity, or post-execution DOM mutation. It is a payload integrity gate at the exact moment of fetch-to-execution, nothing more and nothing less. Pair it with Configuring Content-Security-Policy with SRI to prevent execution of un-hashed inline scripts.
4. Production Implementation Patterns
Permalink to "4. Production Implementation Patterns"Build-Tool Integration
Permalink to "Build-Tool Integration"Webpack via webpack-subresource-integrity:
// webpack.config.js
const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');
module.exports = {
output: {
crossOriginLoading: 'anonymous',
},
plugins: [
new SubresourceIntegrityPlugin({ hashFuncNames: ['sha384'] }),
],
};
The plugin runs post-minification, ensuring the hash covers the exact bytes that will be served. It injects integrity and crossorigin attributes into the HTML output automatically.
Vite via vite-plugin-subresource-integrity:
// vite.config.js
import { defineConfig } from 'vite';
import { ViteSubresourceIntegrity } from 'vite-plugin-subresource-integrity';
export default defineConfig({
plugins: [ViteSubresourceIntegrity({ hashAlgorithms: ['sha384'] })],
build: { cssCodeSplit: false },
});
Manual hash generation (Node.js, for post-build scripts):
const crypto = require('crypto');
const fs = require('fs');
function sriHash(filePath, algorithm = 'sha384') {
const bytes = fs.readFileSync(filePath);
const digest = crypto.createHash(algorithm).update(bytes).digest('base64');
return `${algorithm}-${digest}`;
}
console.log(sriHash('./dist/vendor.js'));
// sha384-oqVuAfXRKap7fdgcCY5uykM6+...
Always call this after the minification/bundling step. Running it against an un-minified source file produces a hash that will never match the served asset.
CI/CD Gating
Permalink to "CI/CD Gating"# .github/workflows/integrity.yml
name: Dependency Integrity
on: [pull_request, push]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22' }
# Validate lockfile hashes; fail on any mismatch
- run: npm ci --ignore-scripts
# Block PRs with high-severity CVEs
- run: npm audit --audit-level=high
# Verify lockfile was not modified during install
- run: git diff --exit-code package-lock.json
--ignore-scripts disables postinstall hooks to prevent arbitrary code execution during CI resolution. The final git diff step catches cases where npm ci silently regenerates or modifies the lockfile.
Server-Side Header Injection (Nginx)
Permalink to "Server-Side Header Injection (Nginx)"location ~* \.(js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin "*";
}
CDN-hosted assets need both immutable caching (so the URL is stable) and unrestricted Access-Control-Allow-Origin (so browsers can perform SRI hash comparison). A content-addressed URL scheme (e.g. vendor.a3f8c.js) ensures that any asset change forces a new URL, invalidating stale SRI hashes in cached HTML.
5. Cross-Cutting Architecture Considerations
Permalink to "5. Cross-Cutting Architecture Considerations"CORS and SRI Interaction
Permalink to "CORS and SRI Interaction"SRI validation of cross-origin resources is impossible without CORS. The browser must be able to read the response body bytes to compute the hash. The required configuration is:
- CDN or origin server responds with
Access-Control-Allow-Origin: *(or a specific allowed origin). - HTML element includes
crossorigin="anonymous". - Both conditions must be met simultaneously; either alone is insufficient.
For resources served from the same origin, neither the crossorigin attribute nor CORS headers are needed — SRI works unconditionally on same-origin fetches.
CSP and SRI Interaction
Permalink to "CSP and SRI Interaction"A strict Content Security Policy without unsafe-inline requires that every executed script either matches a script-src allowlist or carries a valid nonce or SRI hash. The most secure configuration combines both:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com 'sha384-<hash>';
style-src 'self' https://cdn.example.com 'sha384-<hash>';
require-sri-for script style;
require-sri-for script style (a CSP Level 3 directive) instructs the browser to reject any <script> or <link> element that lacks an integrity attribute entirely, providing defense-in-depth beyond per-element hashes.
Dynamic and Async Script Loading
Permalink to "Dynamic and Async Script Loading"Dynamically created <script> elements (via document.createElement('script')) support SRI through the element.integrity property:
const s = document.createElement('script');
s.src = 'https://cdn.example.com/widget.min.js';
s.integrity = 'sha384-<hash>';
s.crossOrigin = 'anonymous';
document.head.appendChild(s);
The browser enforces the integrity check identically to a static <script> tag. Omitting crossOrigin = 'anonymous' on a cross-origin dynamic element is the most common dynamic-loading SRI bug.
For import() dynamic module loading, the current SRI specification does not cover ES module dynamic imports. The Import Maps proposal extends SRI to modules via the integrity key in the import map JSON.
COOP/COEP Isolation
Permalink to "COOP/COEP Isolation"Cross-Origin Opener Policy (COOP: same-origin) and Cross-Origin Embedder Policy (COEP: require-corp) enable SharedArrayBuffer and high-resolution timers but impose additional CORS restrictions. Under COEP, every cross-origin resource must be served with either Cross-Origin-Resource-Policy: cross-origin or a CORS response. This interacts with SRI: a COEP-blocked resource never reaches the browser’s hash comparison step, so the onerror handler fires with a generic network error rather than an integrity error.
6. Compliance Mapping
Permalink to "6. Compliance Mapping"| Framework | Requirement | Supply chain control |
|---|---|---|
| PCI DSS v4.0.1 | Req. 6.4.3 — all payment page scripts must be authorized and their integrity verified | SRI integrity attribute on every script tag; SBOM listing all authorized scripts |
| PCI DSS v4.0.1 | Req. 6.3.2 — maintain a software inventory | CycloneDX or SPDX SBOM generated at build time |
| NIST SSDF | PW.4 — reuse existing, well-secured software | Dependency pinning to exact versions with lockfile hash verification |
| NIST SSDF | PO.5 — implement and maintain secure environments for software development | Ephemeral CI runners with network isolation; --ignore-scripts enforcement |
| SOC 2 Type II | CC6.8 — changes to infrastructure, data, software | Provenance attestation linking source commit to deployed artifact |
| ISO 27001 | A.12.5 — control of software on operational systems | Lockfile drift detection; no unsigned artifacts in production |
| ISO 27001 | A.12.6 — technical vulnerability management | Continuous CVE scanning with runtime-exposure-weighted triage |
PCI DSS v4.0.1 Requirement 6.4.3 is the most directly actionable mandate for SRI adoption: it applies to all scripts on payment pages as of the March 2025 enforcement date. Dependency Pinning Best Practices and Lockfile Mapping & Analysis together satisfy PCI requirements 6.3.2 and 6.4.3 at the dependency and browser-asset layers respectively.
7. Operational Runbook
Permalink to "7. Operational Runbook"Hash Rotation SLA
Permalink to "Hash Rotation SLA"SRI hashes are tied to specific asset bytes. They must be rotated whenever:
- The underlying JavaScript or CSS file content changes (any build producing a new output).
- A CDN URL changes but the path stays the same (re-uploads without content addressing).
- An algorithm migration occurs (SHA-256 → SHA-384).
Rotation procedure:
- Build the new asset artifact.
- Generate the new SHA-384 hash against the final minified bytes.
- Update
integrityattributes in all HTML templates and the hash manifest. - Deploy atomically: the new HTML and the new asset must be live simultaneously.
- Retain the previous asset URL and hash in a rollback manifest for the CDN cache TTL period (minimum 1 hour, recommended 24 hours).
Mismatch Telemetry
Permalink to "Mismatch Telemetry"Browser SRI failures are visible in the browser DevTools console but are not automatically reported back to the server. Implement a SecurityPolicyViolationEvent listener to capture and forward failures to your observability platform:
document.addEventListener('securitypolicyviolation', (e) => {
if (e.violatedDirective === 'script-src' || e.blockedURI) {
navigator.sendBeacon('/api/csp-report', JSON.stringify({
blockedURI: e.blockedURI,
violatedDirective: e.violatedDirective,
originalPolicy: e.originalPolicy,
timestamp: Date.now(),
}));
}
});
Also configure a CSP report-uri or report-to endpoint so that browser-native violation reports reach your SIEM without relying on JavaScript execution.
Rollback Procedure
Permalink to "Rollback Procedure"- On detection of a hash mismatch spike (>1% of page loads in a 5-minute window), trigger an automated rollback in the deployment pipeline.
- Re-deploy the previous HTML artifact from the release manifest.
- Verify the previous CDN asset URL is still cached (CDN purge of the new URL may be needed).
- Investigate the root cause: unauthorized asset modification, premature HTML deployment without the new asset, or CDN misconfiguration.
8. Common Pitfalls and Mitigations
Permalink to "8. Common Pitfalls and Mitigations"| Issue | Root cause | Fix |
|---|---|---|
| SRI check always fails on CDN assets | Missing crossorigin="anonymous" attribute or CDN not sending CORS headers |
Add crossorigin="anonymous" to all SRI-tagged cross-origin elements; configure Access-Control-Allow-Origin on the CDN distribution |
| Hash computed against source file, not built artifact | Hash generated pre-minification | Run hash generation as the final build step, after all transforms |
Lockfile integrity field missing or empty |
Lockfile generated with an old npm version or manually edited | Delete and regenerate with npm install --package-lock-only; upgrade npm ≥7 |
| SBOM not covering transitive dependencies | SBOM tool invoked on package.json only | Use cyclonedx-npm --all or syft . --scope all-layers to traverse the full tree |
unsafe-inline in CSP defeats SRI |
Legacy inline scripts; hasty CSP migration | Refactor inline scripts to external files with SRI hashes; or use nonces as a transitional measure |
| Post-install scripts executing in CI | npm install without --ignore-scripts |
Replace npm install with npm ci --ignore-scripts in all CI jobs |
Exact-version pinning in package.json but floating in lockfile |
npm update run locally without committing lockfile |
Treat lockfile as a versioned artifact; reject PRs that modify it without a corresponding package.json change |
| Provenance attestation not verified at deploy time | SLSA workflow generates attestations but policy engine not configured | Add slsa-verifier verify-artifact step before deployment; configure OPA policy to reject unsigned artifacts |
9. Frequently Asked Questions
Permalink to "9. Frequently Asked Questions"Does SRI protect against a compromised CDN origin?
Yes. SRI blocks execution of any fetched script or stylesheet whose cryptographic digest does not match the declared integrity attribute, regardless of whether the mismatch was caused by CDN compromise, a MitM attack, or accidental file corruption. The attacker would need to produce a payload with exactly the same SHA-384 digest as the legitimate file — computationally infeasible with current hardware.
What is the difference between a lockfile integrity hash and an SRI hash?
A lockfile integrity hash (the integrity field in package-lock.json) validates an npm package tarball at install time inside your CI runner. An SRI hash (the integrity attribute on a <script> tag) validates a browser-fetched asset at runtime inside the user’s browser. Both use SHA-384 but they guard different trust boundaries: the lockfile guards what enters your build; SRI guards what enters the user’s browser.
Is SHA-256 acceptable for SRI, or must I use SHA-384?
The W3C SRI specification allows SHA-256, SHA-384, and SHA-512. SHA-384 is the de-facto standard because it provides 192-bit collision resistance — double SHA-256’s 128-bit — with negligible size overhead, and aligns with PCI DSS v4.0.1 Requirement 6.4.3. New deployments should default to SHA-384. SHA-512 is an acceptable upgrade; SHA-256 is not recommended for new work.
Do I need CORS headers on CDN assets for SRI to work?
Yes, for cross-origin resources. The CDN must serve Access-Control-Allow-Origin: * (or a specific allowed origin) and the HTML element must include crossorigin="anonymous". Without CORS, the browser receives an opaque response and cannot compute the hash for comparison, causing an unconditional failure. Same-origin resources do not require CORS or the crossorigin attribute.
What is SLSA and why does it matter?
SLSA (Supply chain Levels for Software Artifacts) defines four maturity levels for build provenance. At SLSA Level 3, builds run in an isolated, ephemeral CI environment and produce a signed attestation that cryptographically links the source commit to the output artifact. This provenance can be independently verified, preventing scenarios where an attacker modifies artifacts inside a compromised CI runner. Provenance Verification Workflows covers SLSA implementation in detail.
How often should I rotate SRI hashes?
Rotate SRI hashes whenever the underlying asset changes — no more, no less. The correct trigger is your build pipeline: generate hashes post-minification, inject them into HTML at deploy time, and store previous hashes in a rollback manifest for at least one deployment window (typically 24–72 hours) so rollbacks do not break integrity checks. Do not rotate hashes on a calendar schedule; hash correctness is a function of asset content, not time.
Which compliance frameworks explicitly mandate SRI or equivalent controls?
PCI DSS v4.0.1 Requirement 6.4.3 is the most specific: all scripts on payment pages must be authorized and their integrity verified (SRI is the named mechanism). NIST SSDF PW.4 and PO.5 require software provenance and secure development environments. SOC 2 CC6.8 covers change management controls applicable to dependency updates. ISO 27001 A.12.5 and A.12.6 cover software installation controls and vulnerability management.
Related
Permalink to "Related"- Lockfile Mapping & Analysis — parsing and auditing
package-lock.json,yarn.lock, andpnpm-lock.yamlfor unauthorized substitutions - Dependency Pinning Best Practices — exact version pinning, monorepo strategies, and drift detection in pull requests
- Automated SBOM Generation — CycloneDX and SPDX pipeline integration for continuous component inventory
- Provenance Verification Workflows — SLSA attestation, Sigstore signing, and policy-engine enforcement
- Vulnerability Tracking & Triage — CVE ingestion, runtime-exposure weighting, and patch prioritization
- Third-Party Risk Assessment — vendor security posture evaluation and contractual SLA requirements
- Core SRI Fundamentals & Browser Security Boundaries — cryptographic hash algorithms, browser enforcement mechanics, and graceful fallback strategies
In This Section
Lockfile Mapping & Analysis
1 article
Read moreAutomated SBOM Generation
1 article
Read moreDependency Pinning Best Practices
2 articles
Read moreProvenance Verification Workflows
1 article
Read moreContinuous Dependency Monitoring
1 article
Read moreThird-Party Risk Assessment
1 article
Read moreVulnerability Tracking & Triage
2 articles
Read more