CSP的使用

Content Security Policy (CSP) 是一种重要的安全机制,旨在帮助防止跨站脚本 (XSS) 和其他代码注入攻击。它通过允许开发者定义浏览器可以加载哪些资源以及从何处加载这些资源来工作。CSP 可以作为 HTTP 响应头或 <meta> 标签配置。通常,HTTP 响应头是首选方式,因为它提供了更全面的控制和安全性,而 <meta> 标签有一些限制,例如不支持 frame-ancestors、report-to、report-uri 和 sandbox 等指令 12。

CSP 配置基础

CSP 通过一系列指令来定义策略,每个指令控制不同类型的资源加载。这些指令之间用分号 ; 分隔。例如,script-src 用于控制 JavaScript 的来源,style-src 用于控制 CSS 的来源,img-src 用于控制图片的来源等。default-src 指令则为所有未明确指定来源的资源类型设置默认策略 34。

示例:

一个基本的 CSP 策略可能看起来像这样,它只允许从当前域名加载脚本、图片、CSS 和 AJAX 请求,并禁止加载其他任何资源:
Content-Security-Policy: default-src 'self'; 4

其中:

  • 'self':表示允许来自当前源(同协议、同域名、同端口)的资源。需要加引号。
  • *:允许来自任何源的资源(不推荐用于敏感资源如脚本) 7.
  • 'none':禁止加载任何资源。需要加引号 56.

禁止内联脚本

内联脚本(<script> 标签内的代码、HTML 属性中的事件处理程序如 onclick、javascript: URL 以及 eval() 等动态代码执行函数)是 XSS 攻击的常见载体,因为浏览器无法区分合法内联脚本与恶意注入的脚本 38。

默认情况下,严格的 CSP 会禁止内联脚本的执行。这是 CSP 最重要的安全功能之一 58。

要禁止内联脚本,你可以在 script-src 指令中不包含 'unsafe-inline'。如果你的策略中没有显式允许内联脚本,它们就会被禁止。

示例:

以下策略禁止所有内联脚本和 eval():
Content-Security-Policy: script-src 'self'

这个策略只允许加载来自当前域名的外部脚本文件,而任何内联脚本都将被阻止 9.

如果你的代码中存在内联脚本,浏览器会报告 CSP 违规错误,并且这些脚本将不会执行。为了符合 CSP,建议将内联脚本重构为外部文件 8.

允许特定内联脚本

尽管禁止内联脚本是最佳实践,但在某些情况下,你可能无法完全移除所有内联脚本。CSP 提供了两种安全的方式来允许特定的内联脚本执行:Nonce (随机数) 和 Hash (哈希值) 68。

1. 使用 Nonce (随机数)

Nonce 是一种"只使用一次的数字",它是一个加密安全的随机字符串,每次页面加载时都会重新生成。通过将 Nonce 添加到 CSP 策略和对应的 <script> 标签中,你可以允许特定的内联脚本执行 810。

配置步骤:

  1. 服务器端生成 Nonce: 在服务器端为每个请求生成一个唯一的、不可猜测的 Base64 编码随机字符串。这个 Nonce 每次页面加载时都必须不同 810。

    • 例如 (Node.js): const crypto = require("crypto"); const nonce = crypto.randomBytes(16).toString("base64"); 10
  2. 将 Nonce 添加到 CSP 头部: 将生成的 Nonce 值添加到 Content-Security-Policy 响应头的 script-src 指令中,前缀为 'nonce-' 810。

    • Content-Security-Policy: script-src 'nonce-YOUR_GENERATED_NONCE' 'self';
  3. 将 Nonce 添加到 <script> 标签: 在需要允许的内联 <script> 标签上添加 nonce 属性,其值与 CSP 头部中的 Nonce 相同 810。

    • <script nonce="YOUR_GENERATED_NONCE"> /* 你的内联代码 */ </script>

示例:

假设服务器生成了一个 Nonce 值为 EDNnf03nceIOfn39fn3e9h3sdfa。

HTTP 响应头:
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa' 'self'; 8

HTML 中的内联脚本:
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa"> console.log('This inline script is allowed.'); </script> 8

优点: 动态性强,每次请求 Nonce 都不同,攻击者难以预测 11。
缺点: 需要服务器端动态生成和插入 Nonce,对于静态 HTML 文件或 CDN 部署可能不方便 11。

2. 使用 Hash (哈希值)

哈希值是内联脚本内容的 SHA-256、SHA-384 或 SHA-512 散列值。通过计算内联脚本的哈希值并将其添加到 CSP 策略中,可以允许该特定脚本执行 68。

配置步骤:

  1. 计算内联脚本的哈希值:

    • 获取内联 <script> 标签内的所有代码(不包括 <script> 标签本身)。
    • 确保代码内容没有前导或尾随空格,并且大小写敏感。
    • 使用 SHA-256、SHA-384 或 SHA-512 算法计算其 Base64 编码的哈希值 68.
    • 许多工具和浏览器开发者工具(如 Chrome 40+ 的控制台)可以帮助你生成哈希值 812.
  2. 将哈希值添加到 CSP 头部: 将计算出的哈希值添加到 script-src 指令中,前缀为 'sha256-'、'sha384-' 或 'sha512-' 68.

    • Content-Security-Policy: script-src 'sha256-YOUR_SCRIPT_HASH' 'self';

示例:

假设你的 HTML 中有以下内联脚本:
<script>alert('Hello, world.');</script>

这个脚本的 SHA-256 哈希值可能是 qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng=。

HTTP 响应头:
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng=' 'self'; 8

优点: 适用于静态 HTML 页面,无需服务器端动态生成 11。
缺点: 任何对内联脚本内容的微小改动(包括空格和注释)都会改变哈希值,导致脚本被阻止,需要重新计算和更新 CSP 8.

严格 CSP (Strict CSP)

为了更全面地防御 XSS,推荐使用"严格 CSP"。严格 CSP 通常结合 Nonce 或 Hash 以及 strict-dynamic 指令。strict-dynamic 指令允许通过 Nonce 或 Hash 信任的脚本,动态地加载和执行其他脚本,而无需为每个动态加载的脚本显式添加 Nonce 或 Hash 1113。

示例 (基于 Nonce 的严格 CSP):
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none'; 11

其中 {RANDOM} 每次请求都会替换为新的 Nonce。

CSP 报告机制

为了监控 CSP 违规情况,你可以使用 report-uri 或 report-to 指令。当浏览器检测到 CSP 策略被违反时,它会将违规报告(JSON 格式)发送到指定的 URI 1415。

示例:
Content-Security-Policy: default-src 'self'; script-src 'self'; report-uri /csp-report-endpoint; 16

注意: report-uri 已被 report-to 取代,但 report-uri 仍然广泛使用 614. 报告端点应使用 HTTPS 以保护报告数据的隐私 17.

实施建议

  1. 从 Report-Only 模式开始: 在生产环境中强制执行 CSP 之前,建议先使用 Content-Security-Policy-Report-Only 头部。这允许你监控 CSP 违规,而不会阻止任何内容加载,从而帮助你发现并调整策略,避免影响用户体验 14.
    Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report-endpoint;
  2. 逐步收紧策略: 从一个宽松的策略开始,然后逐步收紧,直到所有合法的资源都得到允许,并且所有不必要的资源都被阻止 1.
  3. 避免 'unsafe-inline' 和 'unsafe-eval': 除非绝对必要,否则应避免在 script-src 或 style-src 中使用 'unsafe-inline' 和 'unsafe-eval'。它们会大大削弱 CSP 的保护作用 8.
  4. 定期审查和更新: 随着网站功能的增加和依赖项的变化,CSP 策略也需要定期审查和更新 1.

通过正确配置和使用 CSP,可以显著提高前端应用的安全性,有效抵御 DOM 型 XSS 等攻击。


推荐好文:

  1. 7.2 内容安全策略· PWA 应用实战

  2. 如何用CSP 保护自己的网站 - Plantree

  3. 内容安全政策| Articles - web.dev

  4. nonce - HTML(超文本标记语言) - MDN Web Docs

  5. 使用严格的内容安全政策(CSP) 缓解跨站脚本攻击(XSS) | Articles | web.dev

  6. Adobe Commerce:在内容安全策略(CSP)限制模式下,签出页面出现内联JavaScript问题

  7. CSP: report-uri (CSP) - HTTP 中文开发手册- 开发者手册 - 腾讯云

相关推荐
jktantan3 小时前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
前端·javascript·vue.js
2601_962885724 小时前
quotes 字段全解:涨跌幅、振幅、换手率的口径与单位
java·前端·spring
excel5 小时前
encodeURI 和 encodeURIComponent 有什么区别?
前端
前端精髓5 小时前
调用openrouter的免费API模型
java·前端·javascript
赛博守夜人5 小时前
AI安全攻防与治理之十四:AI 安全测试不能脱离传统安全,Web、API、移动端与模型攻击面的组合拳
前端·人工智能·安全
开开心心_Every5 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
networknomad6 小时前
OpenReplay:自己搭的会话回放工具,给前端排错用的
前端·其他
缓慢更新6 小时前
AI 辅助生成单测:从接口定义到用例的流水线
java·服务器·前端·网络·数据库·人工智能
陈随易7 小时前
AI 运维服务器,安全又可靠的方法
前端·后端·程序员
风骏时光牛马8 小时前
AI稳定性治理体系构建与疑难问题深度分析
前端