JS `Content Security Policy (CSP)` `Strict-CSP` 与 `Trusted Types` 强制执行

各位靓仔靓女,很高兴今天能和大家聊聊前端安全这块硬骨头,尤其是 CSP (Content Security Policy), Strict-CSP 和 Trusted Types 这哥仨,怎么才能把它们驯服得服服帖帖,让我们的网站更安全,让黑客大哥们无从下手。咱们争取用最接地气的方式,把这些概念掰开了揉碎了,让你听完就能上手。

开场白:前端安全,没那么玄乎!

别一听前端安全就觉得高深莫测,好像只有大神才能玩转。其实啊,它就像给你的房子装个防盗门,窗户安个防盗网,就是为了防止坏人进来搞破坏。CSP、Strict-CSP 和 Trusted Types 就像是不同级别的安全措施,级别越高,安全性自然也就越高。

第一部分:CSP (Content Security Policy) – 网站的“安检员”

CSP 是什么?简单来说,它就是一个 HTTP 响应头,告诉浏览器哪些资源是允许加载的,哪些是不允许的。想象一下,你的网站就是个机场,CSP 就是安检员,负责检查乘客(资源)是否携带违禁品(恶意代码)。

1. CSP 的基本语法

CSP 的语法其实挺简单的,就是一堆指令,每个指令后面跟着允许的来源。例如:

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

这条规则的意思是:

  • default-src 'self': 默认情况下,只允许加载来自同源的资源。
  • script-src 'self' https://example.com: 允许加载来自同源和 https://example.com 的 JavaScript 脚本。
  • style-src 'self' 'unsafe-inline': 允许加载来自同源的 CSS,并且允许使用行内样式 (<style> 标签和 style 属性)。注意 'unsafe-inline' 非常危险,尽量避免使用!
  • img-src 'self' data:: 允许加载来自同源的图片,并且允许使用 data: URI。

2. 常用指令详解

  • default-src: 所有类型资源的默认来源。如果某个资源类型没有明确指定来源,就使用 default-src
  • script-src: JavaScript 脚本的来源。
  • style-src: CSS 样式的来源。
  • img-src: 图片的来源。
  • font-src: 字体的来源。
  • connect-src: 允许 XMLHttpRequest (AJAX), WebSocket 和 EventSource 连接的来源。
  • media-src: 音视频的来源。
  • object-src: <object>, <embed><applet> 元素的来源。
  • frame-src: <frame><iframe> 元素的来源。
  • base-uri: 允许 <base> 元素使用的 URI。
  • form-action: 允许 <form> 元素提交到的 URI。
  • upgrade-insecure-requests: 指示浏览器将所有 HTTP 请求升级为 HTTPS。
  • block-all-mixed-content: 阻止加载任何使用 HTTP 协议的资源,当网站使用 HTTPS 时。
  • report-uri: 指定一个 URI,浏览器会将违反 CSP 策略的报告发送到这个 URI。
  • report-to: 指定一个组名,浏览器会将违反 CSP 策略的报告发送到这个组。配合 Report-To HTTP 响应头使用。

3. CSP 的两种模式:Content-Security-PolicyContent-Security-Policy-Report-Only

  • Content-Security-Policy: 强制执行策略,阻止违反策略的资源加载。
  • Content-Security-Policy-Report-Only: 不强制执行策略,只是将违反策略的报告发送到指定的 URI。这个模式通常用于测试和调试 CSP 策略。

4. 实际应用:一个简单的 CSP 例子

假设我们有一个简单的 HTML 页面:

<!DOCTYPE html>
<html>
<head>
  <title>CSP Example</title>
  <style>
    body {
      background-color: #f0f0f0;
    }
  </style>
</head>
<body>
  <h1>Hello, CSP!</h1>
  <img src="image.jpg" alt="Image">
  <script>
    console.log("Hello from inline script!");
  </script>
  <script src="script.js"></script>
</body>
</html>

我们想要限制只能加载来自同源的资源,并且不允许使用行内样式和行内脚本。我们可以设置以下 CSP 响应头:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self';

设置了这个 CSP 之后,浏览器会阻止行内脚本的执行,并且只允许加载来自同源的 script.js。同时,也会阻止行内样式的使用,页面背景色将不会生效。

5. CSP 的注意事项

  • 严格模式: 在部署 CSP 时,一定要从 Content-Security-Policy-Report-Only 模式开始,观察一段时间,确保没有遗漏的资源,然后再切换到 Content-Security-Policy 模式。

  • 白名单: 尽量使用白名单,而不是黑名单。白名单只允许指定的来源,而黑名单阻止指定的来源,更容易被绕过。

  • Nonce 和 Hash: 对于必须使用的行内脚本和样式,可以使用 noncehash 属性来授权。

    • nonce 是一个随机字符串,每次页面加载时都会生成一个新的 nonce 值。
    • hash 是脚本或样式的 SHA256, SHA384 或 SHA512 哈希值。

    例如:

    <script nonce="R4andom">
      console.log("Hello from inline script!");
    </script>
    Content-Security-Policy: script-src 'nonce-R4andom';

    或者:

    <script>
      console.log("Hello from inline script!");
    </script>
    Content-Security-Policy: script-src 'sha256-your-script-hash';
  • CDN: 如果使用了 CDN,需要将 CDN 的域名添加到 CSP 指令中。

  • 兼容性: 不同的浏览器对 CSP 的支持程度可能不同,需要进行兼容性测试。

第二部分:Strict-CSP – 更严格的安全卫士

Strict-CSP 是 CSP 的一个更严格的版本,它旨在消除 CSP 配置中的一些模糊性,并提供更强的安全性。它主要通过使用 'strict-dynamic' 关键字来实现。

1. 'strict-dynamic' 关键字

'strict-dynamic' 关键字允许浏览器信任由服务器明确信任的脚本创建的脚本。这意味着,如果一个脚本是通过 CSP 授权的脚本动态创建的,那么这个动态创建的脚本也会被信任。

2. Strict-CSP 的基本语法

Strict-CSP 的语法与 CSP 类似,但是需要包含 'strict-dynamic' 关键字。例如:

Content-Security-Policy: require-trusted-types-for 'script'; script-src 'self' 'strict-dynamic' 'nonce-R4andom';

这条规则的意思是:

  • require-trusted-types-for 'script': 要求使用 Trusted Types 来处理脚本。
  • script-src 'self' 'strict-dynamic' 'nonce-R4andom': 允许加载来自同源的 JavaScript 脚本,并且允许通过 CSP 授权的脚本动态创建的脚本,以及使用 nonce 属性的行内脚本。

3. Strict-CSP 的优势

  • 更强的安全性: 通过使用 'strict-dynamic' 关键字,可以减少攻击者利用 XSS 漏洞的机会。
  • 更简单的配置: Strict-CSP 的配置通常比传统的 CSP 配置更简单,更容易维护。

4. Strict-CSP 的注意事项

  • 兼容性: 'strict-dynamic' 关键字的兼容性可能不如传统的 CSP 指令,需要进行兼容性测试。
  • Trusted Types: Strict-CSP 通常与 Trusted Types 一起使用,以提供更强的安全性。

第三部分:Trusted Types – 驯服 DOM 操作的野马

Trusted Types 是一种 Web API,旨在防止 DOM-based XSS 攻击。它通过要求开发者在使用某些危险的 DOM API 之前,必须先对数据进行“类型化”,从而减少了 XSS 漏洞的风险。

1. 什么是 DOM-based XSS?

DOM-based XSS 是一种 XSS 攻击,攻击者通过修改页面的 DOM 结构来注入恶意代码。例如,攻击者可以修改 location.hashdocument.referrer,从而导致恶意代码被执行。

2. Trusted Types 的工作原理

Trusted Types 通过引入以下概念来防止 DOM-based XSS 攻击:

  • Trusted Types Policy: 一个 Trusted Types Policy 定义了一组规则,用于创建 Trusted Types 对象。
  • Trusted Types 对象: Trusted Types 对象是经过类型化的数据,可以安全地传递给某些危险的 DOM API。
  • DOM 接口拦截: Trusted Types 会拦截某些危险的 DOM API,例如 innerHTML, outerHTML, src, href 等。只有当传递给这些 API 的数据是 Trusted Types 对象时,才能成功执行。

3. Trusted Types 的基本用法

首先,我们需要创建一个 Trusted Types Policy:

if (window.trustedTypes && window.trustedTypes.createPolicy) {
  const policy = window.trustedTypes.createPolicy('myPolicy', {
    createHTML: (string) => string.replace(/</g, '&lt;'), // 过滤 HTML 标签
    createScriptURL: (string) => {
      if (string.startsWith('https://example.com/')) {
        return string;
      }
      throw new Error('Untrusted URL');
    },
    createScript: (string) => string, // 允许执行任何脚本 (谨慎使用)
  });
}

这个 Policy 定义了三个函数:

  • createHTML: 用于创建 TrustedHTML 对象,对 HTML 字符串进行过滤,防止 XSS 攻击。
  • createScriptURL: 用于创建 TrustedScriptURL 对象,只允许加载来自 https://example.com/ 的脚本。
  • createScript: 用于创建 TrustedScript 对象,允许执行任何脚本 (谨慎使用)。

然后,我们可以使用这些函数来创建 Trusted Types 对象:

const untrustedHTML = '<img src=x onerror=alert(1)>';
const trustedHTML = policy.createHTML(untrustedHTML);

const untrustedURL = 'http://example.com/evil.js';
const trustedURL = policy.createScriptURL(untrustedURL); // 会抛出错误

const trustedSafeURL = policy.createScriptURL('https://example.com/safe.js');

document.getElementById('container').innerHTML = trustedHTML;
const script = document.createElement('script');
script.src = trustedSafeURL;
document.body.appendChild(script);

4. Trusted Types 的注意事项

  • 兼容性: Trusted Types 的兼容性可能不如传统的 CSP 指令,需要进行兼容性测试。
  • 策略设计: Trusted Types Policy 的设计非常重要,需要根据具体的应用场景进行定制。
  • 与 CSP 结合使用: Trusted Types 通常与 CSP 结合使用,以提供更强的安全性。 例如,可以使用 require-trusted-types-for 'script' 指令来要求使用 Trusted Types 处理脚本。

第四部分:CSP、Strict-CSP 和 Trusted Types 的最佳实践

  • 循序渐进:Content-Security-Policy-Report-Only 模式开始,逐步加强 CSP 策略。
  • 最小权限原则: 只允许加载必要的资源,尽量减少白名单的数量。
  • 使用 Nonce 和 Hash: 对于必须使用的行内脚本和样式,使用 noncehash 属性来授权。
  • 结合 Trusted Types: 使用 Trusted Types 来防止 DOM-based XSS 攻击。
  • 定期审查: 定期审查 CSP 策略和 Trusted Types Policy,确保它们仍然有效。
  • 自动化测试: 使用自动化测试工具来验证 CSP 策略和 Trusted Types Policy 的正确性。

第五部分:总结

CSP、Strict-CSP 和 Trusted Types 是前端安全的重要组成部分。通过合理地配置这些安全措施,可以有效地防止 XSS 攻击,提高网站的安全性。

安全措施 作用 优点 缺点
CSP 限制浏览器加载资源的来源,防止 XSS 攻击。 能够有效地防止 XSS 攻击;可以控制资源的加载来源;可以报告违反策略的行为。 配置复杂;兼容性问题;可能会影响网站的性能。
Strict-CSP CSP 的更严格版本,旨在消除 CSP 配置中的一些模糊性,并提供更强的安全性。 更强的安全性;更简单的配置。 兼容性问题;需要与 Trusted Types 结合使用。
Trusted Types 防止 DOM-based XSS 攻击,通过要求开发者在使用某些危险的 DOM API 之前,必须先对数据进行“类型化”。 能够有效地防止 DOM-based XSS 攻击;可以更好地控制数据的类型。 兼容性问题;策略设计复杂;需要与 CSP 结合使用。

结束语:安全之路,永无止境!

前端安全是一个持续学习和实践的过程。希望今天的分享能够帮助大家更好地理解 CSP、Strict-CSP 和 Trusted Types,并在实际项目中应用这些安全措施,让我们的网站更加安全可靠!记住,安全不是一蹴而就的,需要我们不断地学习和进步!

有问题尽管问,我尽量解答! 祝大家编码愉快,远离 Bug!

发表回复

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