Subresource Integrity (SRI)

Security feature that enables browsers to verify that fetched resources have not been tampered with.

What is Subresource Integrity (SRI)?

Subresource Integrity (SRI) is a security feature that enables browsers to verify that resources (such as scripts and stylesheets) fetched from external sources have not been tampered with. SRI works by allowing developers to provide cryptographic hashes that browsers can use to validate the integrity of fetched resources.

When a browser loads a resource with an SRI hash, it calculates the hash of the received content and compares it to the expected hash. If they don't match, the browser refuses to execute or apply the resource, protecting users from potentially malicious content.

How SRI Works

  1. Developer generates a hash of the resource content
  2. Hash is included in the HTML element that loads the resource
  3. Browser fetches the resource from the external source
  4. Browser calculates the hash of the received content
  5. Browser compares hashes - if they match, the resource is used; if not, it's blocked

Syntax and Usage

SRI uses the integrity attribute on <script> and <link> elements:

<script src="https://example.com/library.js"
        integrity="sha384-uniqueHashValueHere"
        crossorigin="anonymous"></script>

<link rel="stylesheet" href="https://example.com/styles.css"
      integrity="sha384-uniqueHashValueHere"
      crossorigin="anonymous">

Supported Hash Algorithms

SRI supports three cryptographic hash algorithms:

  1. SHA-256: sha256-HASHVALUE
  2. SHA-384: sha384-HASHVALUE (recommended)
  3. SHA-512: sha512-HASHVALUE

Security Benefits

Prevents resource tampering:

  • Protects against CDN compromises
  • Prevents man-in-the-middle attacks
  • Blocks malicious resource injection

Enhances supply chain security:

  • Verifies third-party resources
  • Protects against compromised dependencies
  • Ensures resource authenticity

Mitigates various attacks:

Implementation Examples

Basic SRI implementation:

<script src="https://cdn.example.com/library.js"
        integrity="sha384-abc123..."
        crossorigin="anonymous"></script>

Multiple hash values (for fallback):

<script src="https://cdn.example.com/library.js"
        integrity="sha384-abc123... sha512-def456..."
        crossorigin="anonymous"></script>

With stylesheets:

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

Generating SRI Hashes

You can generate SRI hashes using various tools:

Using OpenSSL:

cat library.js | openssl dgst -sha384 -binary | openssl base64 -A

Using Node.js:

const crypto = require('crypto');
const fs = require('fs');

const file = fs.readFileSync('library.js');
const hash = crypto.createHash('sha384').update(file).digest('base64');
console.log(`sha384-${hash}`);

Online tools:

Best Practices

  1. Always use SRI for third-party resources - especially from CDNs
  2. Use SHA-384 as the preferred hash algorithm
  3. Include the crossorigin="anonymous" attribute for proper CORS handling
  4. Update hashes when resources change - outdated hashes will cause resources to be blocked
  5. Use SRI for all critical resources - scripts, stylesheets, and other executable content
  6. Combine with other security measures:

Common Use Cases

  1. CDN-hosted libraries: jQuery, Bootstrap, React, etc.
  2. Third-party scripts: Analytics, advertising, social media widgets
  3. External stylesheets: CSS frameworks, icon libraries
  4. Web fonts: Google Fonts, Font Awesome
  5. Critical application resources: Main application scripts and styles

Browser Support

SRI is supported in all modern browsers:

  • Chrome 45+
  • Firefox 43+
  • Edge 17+
  • Safari 11+
  • Opera 32+

Debugging SRI Issues

Common SRI-related issues and solutions:

  1. Resource blocked due to hash mismatch:
    • Verify the resource hasn't been modified
    • Regenerate the hash with the current resource content
    • Check for whitespace or encoding differences
  2. CORS errors with SRI:
    • Ensure crossorigin="anonymous" is set
    • Verify the server sends proper CORS headers
    • Check for mixed content issues (HTTP vs HTTPS)
  3. Resource not loading at all:
    • Check browser console for specific error messages
    • Verify the hash algorithm is supported
    • Ensure the integrity attribute syntax is correct
  4. Performance impact:
    • SRI adds minimal overhead for hash calculation
    • Most impact comes from additional network requests
    • Consider preloading critical resources

Advanced SRI Usage

Dynamic SRI with build tools:

// Webpack plugin example
const SriPlugin = require('webpack-subresource-integrity');
module.exports = {
  output: {
    crossOriginLoading: 'anonymous',
  },
  plugins: [
    new SriPlugin({
      hashFuncNames: ['sha384'],
    }),
  ],
};

SRI with service workers:

// Service worker can verify SRI hashes before caching
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('my-cache').then((cache) => {
      return cache.addAll([
        {
          url: 'https://cdn.example.com/library.js',
          integrity: 'sha384-abc123...'
        }
      ]);
    })
  );
});

SRI with Content Security Policy:

Content-Security-Policy: require-sri-for script style;

Example: Complete Secure Implementation

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Secure Application</title>

  <!-- Secure stylesheet with SRI -->
  <link rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
        integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
        crossorigin="anonymous">

  <!-- Secure script with SRI -->
  <script src="https://code.jquery.com/jquery-3.7.0.min.js"
          integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs"
          crossorigin="anonymous"></script>

  <!-- Local script with SRI -->
  <script src="/js/app.js"
          integrity="sha384-YourGeneratedHashHere"></script>
</head>
<body>
  <!-- Content -->
</body>
</html>