JS `Subresource Integrity (SRI)` 的 `Content-Security-Policy` 集成与自动化

admin
Sun, 20 Jul 2025 10:04:59 +0000
Javascript, integrity, js, subresource

大家好！欢迎来到今天的“JS Subresource Integrity (SRI) 的 Content-Security-Policy 集成与自动化”讲座。 我是你们今天的导游，负责带大家一起探索如何让我们的网站更安全，更可靠。准备好了吗？Let’s roll! 第一幕：故事的开始 – 为什么我们需要 SRI 和 CSP？ 想象一下，你辛辛苦苦搭建了一个完美的网站，代码写得像诗一样优美。突然有一天，你发现你的网站开始弹广告，或者更糟糕，用户的信用卡信息被盗了！原因可能就是因为你引用的第三方库被人篡改了。 这就是为什么我们需要 Subresource Integrity (SRI) 和 Content-Security-Policy (CSP)。它们就像我们网站的保安，负责检查进出我们网站的每个“人”（资源），确保它们都是好人。 SRI (Subresource Integrity): 验证从 CDN 或其他源加载的资源是否未被篡改。简单来说，就是给每个资源加上一个“指纹”，浏览器加载时会检查这个指纹是否匹配。如果指纹不一样，说明资源被人动过手脚，浏览器就会拒绝加载。 CS …

继续阅读“JS `Subresource Integrity (SRI)` 的 `Content-Security-Policy` 集成与自动化”

The post JS `Subresource Integrity (SRI)` 的 `Content-Security-Policy` 集成与自动化 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

大家好！欢迎来到今天的“JS Subresource Integrity (SRI) 的 Content-Security-Policy 集成与自动化”讲座。

我是你们今天的导游，负责带大家一起探索如何让我们的网站更安全，更可靠。准备好了吗？Let’s roll!

第一幕：故事的开始 – 为什么我们需要 SRI 和 CSP？

想象一下，你辛辛苦苦搭建了一个完美的网站，代码写得像诗一样优美。突然有一天，你发现你的网站开始弹广告，或者更糟糕，用户的信用卡信息被盗了！原因可能就是因为你引用的第三方库被人篡改了。

这就是为什么我们需要 Subresource Integrity (SRI) 和 Content-Security-Policy (CSP)。它们就像我们网站的保安，负责检查进出我们网站的每个“人”（资源），确保它们都是好人。

SRI (Subresource Integrity): 验证从 CDN 或其他源加载的资源是否未被篡改。简单来说，就是给每个资源加上一个“指纹”，浏览器加载时会检查这个指纹是否匹配。如果指纹不一样，说明资源被人动过手脚，浏览器就会拒绝加载。

CSP (Content-Security-Policy): 定义浏览器可以加载哪些来源的资源。相当于给浏览器列了一个“白名单”，只有名单上的“好人”才能进入我们的网站。

第二幕：SRI – 给你的资源加上“指纹”

SRI 的核心在于生成资源的哈希值，并将其添加到 或 标签中。

1. 如何生成哈希值？

可以使用 openssl 命令，或者任何在线 SRI 哈希生成器。

使用 openssl 命令:

openssl dgst -sha384 -binary your_script.js | openssl base64 -A

这个命令会生成 your_script.js 文件的 SHA-384 哈希值。你可以根据需要选择不同的哈希算法，例如 sha256 或 sha512。

注意: 推荐使用 sha384 或 sha512，因为它们更安全。

在线 SRI 哈希生成器: 有很多在线工具可以帮你生成 SRI 哈希值。只需要上传你的文件，或者输入 URL，就可以得到哈希值。

2. 如何将哈希值添加到 HTML 标签？

将生成的哈希值添加到 或 标签的 integrity 属性中。

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

src: 资源的 URL。

integrity: 资源的哈希值。

crossorigin="anonymous": 告诉浏览器使用匿名模式加载资源。这是为了避免 CORS 问题，因为 SRI 会检查响应头的 Access-Control-Allow-Origin 字段。如果你的 CDN 配置了正确的 CORS 头，也可以使用 crossorigin="use-credentials"。

重要提示:

crossorigin 属性是必需的，否则 SRI 无法正常工作。

确保哈希值与资源的实际内容匹配。 否则，浏览器会拒绝加载资源。

一个完整的例子：

假设我们有一个名为 app.js 的文件，内容如下：

console.log("Hello from app.js!");

我们使用 openssl 命令生成它的 SHA-384 哈希值：

openssl dgst -sha384 -binary app.js | openssl base64 -A

假设生成的哈希值是：jZ4jH4U+E6w9o/1+2Q+l0B6jW0jH4U+E6w9o/1+2Q+l0B6jW0jH4U+E6w9o/1+2Q+l0B6jW0jH4U+E6w9o/1+2Q+l0B6jW0jH4U= (这是一个假的例子，不要直接使用)

那么，HTML 代码应该如下所示：

<script
  src="app.js"
  integrity="sha384-jZ4jH4U+E6w9o/1+2Q+l0B6jW0jH4U+E6w9o/1+2Q+l0B6jW0jH4U+E6w9o/1+2Q+l0B6jW0jH4U+E6w9o/1+2Q+l0B6jW0jH4U="
  crossorigin="anonymous"
>

现在，浏览器在加载 app.js 时，会验证它的哈希值是否与 integrity 属性中指定的哈希值匹配。如果匹配，说明资源没有被篡改，浏览器会正常加载。如果不匹配，浏览器会拒绝加载资源，并在控制台中显示错误信息。

第三幕：CSP – 给你的网站设置“门卫”

CSP 通过 HTTP 响应头或 HTML 标签来定义。

1. HTTP 响应头:

这是推荐的方式，因为它更安全。

Content-Security-Policy: policy-directive; policy-directive

2. HTML 标签:

<meta
  http-equiv="Content-Security-Policy"
  content="policy-directive; policy-directive"
/>

policy-directive 是 CSP 的核心，它定义了允许加载哪些来源的资源。

一些常用的 policy-directive：

指令 | 描述 | 例子
default-src | 定义所有资源的默认来源。 | default-src 'self' (只允许同源资源)
script-src | 定义 JavaScript 资源的来源。 | script-src 'self' https://cdn.example.com (允许同源资源和 cdn.example.com 的资源)
style-src | 定义 CSS 资源的来源。 | style-src 'self' 'unsafe-inline' (允许同源资源和内联样式)
img-src | 定义图片资源的来源。 | img-src 'self' data: (允许同源资源和 data URI)
connect-src | 定义可以进行网络请求的来源。 | connect-src 'self' wss://example.com (允许同源请求和 WebSocket 连接到 example.com)
font-src | 定义字体资源的来源。 | font-src 'self' https://fonts.example.com (允许同源资源和 fonts.example.com 的字体)
media-src | 定义音视频资源的来源。 | media-src 'self' (只允许同源音视频资源)
object-src | 定义 , 和 <applet> 元素的来源。 | object-src 'none' (不允许加载任何插件)
frame-src | 定义 和 元素的来源。 | frame-src 'self' https://youtube.com (允许同源 frame 和来自 youtube.com 的 frame)
base-uri | 定义可以出现在 元素中的 URL。 | base-uri 'self' (只允许同源 URL 作为 base URI)
form-action | 定义表单可以提交到哪些 URL。 | form-action 'self' https://secure.example.com (允许提交到同源 URL 和 secure.example.com)
frame-ancestors | 定义当前页面可以被嵌入到哪些来源的 frame 中。 | frame-ancestors 'none' (不允许被任何 frame 嵌入), frame-ancestors 'self' (只允许被同源 frame 嵌入)
report-uri | 定义浏览器在违反 CSP 策略时，将报告发送到哪个 URL。 | report-uri /csp-report (将报告发送到 /csp-report 路径)
report-to | 定义浏览器在违反 CSP 策略时，将报告发送到哪个命名终端点。这个指令和 report-uri 配合使用，可以提供更详细的报告信息，并支持浏览器发送 JSON 格式的报告。 | report-to "default" (将报告发送到名为 "default" 的终端点)

一些常用的来源值：

'self': 允许同源资源。

'none': 不允许任何来源的资源。

'unsafe-inline': 允许内联 JavaScript 和 CSS。（不推荐使用，因为它会降低安全性）

'unsafe-eval': 允许使用 eval() 和 Function()。（不推荐使用，因为它会降低安全性）

'strict-dynamic': 允许通过已经信任的脚本加载的脚本执行。需要配合 nonce 或 hash 使用。

'unsafe-hashes': 允许特定的内联事件处理程序（例如 onclick）使用哈希值。通常不推荐使用。

data:: 允许 data URI。

https://example.com: 允许来自 example.com 的资源。

*.example.com: 允许来自 example.com 及其所有子域的资源。

一个例子：

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:

这个 CSP 策略的含义是：

default-src 'self': 默认情况下，只允许加载同源资源。

script-src 'self' https://cdn.example.com: 允许加载同源的 JavaScript 资源和来自 cdn.example.com 的 JavaScript 资源。

style-src 'self' 'unsafe-inline': 允许加载同源的 CSS 资源和内联样式。

img-src 'self' data:: 允许加载同源的图片资源和 data URI。

重要提示:

'unsafe-inline' 和 'unsafe-eval' 会降低安全性，应该尽量避免使用。

CSP 策略应该根据你的网站的需求进行定制。 不要盲目复制别人的策略。

可以使用 report-uri 或 report-to 指令来收集 CSP 违规报告。 这可以帮助你发现并修复潜在的安全问题。

使用 report-uri 或 report-to 的例子:

Content-Security-Policy: default-src 'self'; report-uri /csp-report; report-to "default";

// 后端处理 CSP 违规报告的示例代码 (Node.js)
app.post('/csp-report', (req, res) => {
  console.log('CSP Violation Report:', req.body);
  // 可以将报告保存到数据库，或者发送到监控系统
  res.sendStatus(204); // 必须返回 204 No Content
});

report-to 指令需要配置一个终端点:

Content-Security-Policy: default-src 'self'; report-to "default";
Report-To: {"group":"default","max_age":31536000,"endpoints":[{"url":"/csp-endpoint"}]}

在这个例子中，Report-To 头定义了一个名为 "default" 的终端点，它会将报告发送到 /csp-endpoint。 max_age 指定了浏览器缓存这个终端点配置的时间（单位是秒）。

CSP 的 nonce 和 hash：

为了更安全地使用内联脚本和样式，可以使用 nonce 或 hash。

nonce (Number used Once): 是一个每次页面加载时都会变化的随机字符串。


  console.log("Hello from inline script!");


Content-Security-Policy: script-src 'nonce-YOUR_RANDOM_STRING'

hash: 是内联脚本或样式的哈希值。


  console.log("Hello from inline script!");


Content-Security-Policy: script-src 'sha256-YOUR_HASH_VALUE'

第四幕：SRI 和 CSP 的集成

SRI 和 CSP 可以一起使用，以提供更强大的安全保护。

1. 使用 SRI 确保第三方库的完整性。

2. 使用 CSP 限制可以加载资源的来源。

一个例子：

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

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com

在这个例子中，我们使用 SRI 确保 your_script.js 文件的完整性，并使用 CSP 限制只能从 cdn.example.com 加载 JavaScript 资源。

第五幕：自动化 – 让你的生活更轻松

手动管理 SRI 和 CSP 策略非常繁琐，容易出错。因此，我们需要自动化工具来帮助我们。

1. 构建工具插件 (Webpack, Parcel, Rollup):

这些工具可以自动生成 SRI 哈希值，并将其添加到 HTML 标签中。它们还可以帮助你生成 CSP 策略。

Webpack: 可以使用 webpack-subresource-integrity 插件。

Parcel: 内置了 SRI 支持。

Rollup: 可以使用 rollup-plugin-sri-hash 插件。

2. 后端框架 (Node.js, Python, Ruby):

这些框架可以自动生成 CSP 响应头。

Node.js: 可以使用 helmet 中间件。

Python: 可以使用 Flask-CSP 扩展。

Ruby on Rails: 可以使用 secure_headers gem。

3. CI/CD 集成:

可以将 SRI 和 CSP 集成到你的 CI/CD 流程中，以便在每次部署时自动生成和验证策略。

一个使用 Webpack 和 webpack-subresource-integrity 插件的例子：

1. 安装插件:

npm install webpack-subresource-integrity --save-dev

2. 配置 webpack.config.js:

const SriPlugin = require('webpack-subresource-integrity');

module.exports = {
  // ...
  plugins: [
    new SriPlugin({
      hashFuncNames: ['sha384'], // 可以选择不同的哈希算法
      enabled: process.env.NODE_ENV === 'production', // 只在生产环境启用
    }),
  ],
};

3. 构建你的项目:

webpack

Webpack 会自动生成 SRI 哈希值，并将其添加到 HTML 标签中。

一个使用 Node.js 和 helmet 中间件的例子：

1. 安装 helmet:

npm install helmet --save

2. 配置你的 Express 应用:

const express = require('express');
const helmet = require('helmet');

const app = express();

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", 'https://cdn.example.com'],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", 'data:'],
      },
    },
  })
);

// ...

helmet 会自动生成 CSP 响应头，并将其添加到每个响应中。

第六幕：最佳实践

从宽松的 CSP 策略开始，然后逐渐收紧。 这可以避免因为 CSP 策略过于严格而导致网站无法正常工作。

使用 CSP 违规报告来监控你的网站。 这可以帮助你发现并修复潜在的安全问题。

定期更新你的第三方库。 这可以确保你使用的是最新版本，并且已经修复了已知的安全漏洞。

使用 SRI 来验证第三方库的完整性。 这可以防止你的网站受到恶意代码的攻击。

自动化你的 SRI 和 CSP 策略。 这可以减少手动操作的错误，并提高效率。

在所有环境中（开发、测试、生产）启用 SRI 和 CSP。 这可以确保你的网站在所有环境中都受到保护。

测试你的 CSP 策略。 可以使用在线 CSP 评估工具或浏览器扩展来测试你的 CSP 策略。

了解你的 CDN 的 CORS 配置。 确保你的 CDN 配置了正确的 CORS 头，以便 SRI 可以正常工作。

总结

SRI 和 CSP 是保护你的网站免受恶意攻击的重要工具。通过合理地使用它们，你可以大大提高你的网站的安全性。记住，安全是一个持续的过程，需要不断地学习和改进。

希望今天的讲座对你有所帮助。感谢大家的参与！

最后，送大家一句安全箴言： "Better safe than sorry!" (小心驶得万年船!)