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

大家好!欢迎来到今天的“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 的核心在于生成资源的哈希值,并将其添加到 <script><link> 标签中。

1. 如何生成哈希值?

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

  • 使用 openssl 命令:

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

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

    注意: 推荐使用 sha384sha512,因为它们更安全。

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

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

将生成的哈希值添加到 <script><link> 标签的 integrity 属性中。

<script
  src="https://cdn.example.com/your_script.js"
  integrity="sha384-YOUR_HASH_VALUE"
  crossorigin="anonymous"
></script>
  • 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"
></script>

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

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

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

1. HTTP 响应头:

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

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

2. HTML <meta> 标签:

<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 定义 <object>, <embed><applet> 元素的来源。 object-src 'none' (不允许加载任何插件)
frame-src 定义 <frame><iframe> 元素的来源。 frame-src 'self' https://youtube.com (允许同源 frame 和来自 youtube.com 的 frame)
base-uri 定义可以出现在 <base> 元素中的 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-urireport-to 指令来收集 CSP 违规报告。 这可以帮助你发现并修复潜在的安全问题。

使用 report-urireport-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-endpointmax_age 指定了浏览器缓存这个终端点配置的时间(单位是秒)。

CSP 的 noncehash

为了更安全地使用内联脚本和样式,可以使用 noncehash

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

    <script nonce="YOUR_RANDOM_STRING">
      console.log("Hello from inline script!");
    </script>
    Content-Security-Policy: script-src 'nonce-YOUR_RANDOM_STRING'
  • hash: 是内联脚本或样式的哈希值。

    <script>
      console.log("Hello from inline script!");
    </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"
></script>
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!" (小心驶得万年船!)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注