CSP的使用

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

CSP 配置基础

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

示例:

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

其中:

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

禁止内联脚本

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

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

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

示例:

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

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

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

允许特定内联脚本

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

1. 使用 Nonce (随机数)

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

配置步骤:

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

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

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

    • <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 策略中,可以允许该特定脚本执行 [6][8]

配置步骤:

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

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

    • 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 [11][13]

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

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

CSP 报告机制

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

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

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

实施建议

  1. 从 Report-Only 模式开始: 在生产环境中强制执行 CSP 之前,建议先使用 Content-Security-Policy-Report-Only 头部。这允许你监控 CSP 违规,而不会阻止任何内容加载,从而帮助你发现并调整策略,避免影响用户体验 [1][4].
    Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report-endpoint;
  2. 逐步收紧策略: 从一个宽松的策略开始,然后逐步收紧,直到所有合法的资源都得到允许,并且所有不必要的资源都被阻止 [1].
  3. 避免 'unsafe-inline''unsafe-eval' 除非绝对必要,否则应避免在 script-srcstyle-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 中文开发手册- 开发者手册 - 腾讯云

相关推荐
10年前端老司机1 小时前
什么!纯前端也能识别图片中的文案、还支持100多个国家的语言
前端·javascript·vue.js
摸鱼仙人~1 小时前
React 性能优化实战指南:从理论到实践的完整攻略
前端·react.js·性能优化
程序员阿超的博客2 小时前
React动态渲染:如何用map循环渲染一个列表(List)
前端·react.js·前端框架
magic 2452 小时前
模拟 AJAX 提交 form 表单及请求头设置详解
前端·javascript·ajax
小小小小宇7 小时前
前端 Service Worker
前端
只喜欢赚钱的棉花没有糖7 小时前
http的缓存问题
前端·javascript·http
小小小小宇8 小时前
请求竞态问题统一封装
前端
loriloy8 小时前
前端资源帖
前端
源码超级联盟8 小时前
display的block和inline-block有什么区别
前端
GISer_Jing8 小时前
前端构建工具(Webpack\Vite\esbuild\Rspack)拆包能力深度解析
前端·webpack·node.js