阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 使用 CSP(内容安全策略)防范 XSS

使用 CSP(内容安全策略)防范 XSS

作者:陈川 阅读数:61949人阅读 分类: 前端安全

什么是 CSP

CSP(Content Security Policy)是一种由浏览器实现的安全机制,用于检测和缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。它通过定义允许加载资源的来源,限制浏览器只能执行或渲染来自这些来源的资源。CSP 的核心思想是白名单机制,开发者明确告诉浏览器哪些资源可以被加载,其他资源一律禁止。

CSP 如何防范 XSS

XSS 攻击通常通过注入恶意脚本到网页中执行。CSP 通过以下方式有效防范 XSS:

  1. 禁止内联脚本执行:默认情况下,CSP 会阻止 <script> 标签中的内联代码和 javascript: 伪协议
  2. 限制脚本来源:只允许从特定域加载脚本文件
  3. 阻止 eval 类函数:禁用 eval()setTimeout(string) 等动态代码执行方式
  4. 限制非静态资源:控制字体、框架、图片等资源的加载源

实现 CSP 的三种方式

通过 HTTP 头实现

最推荐的方式是通过 HTTP 响应头设置:

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src *; media-src media1.com media2.com; report-uri /csp-violation-report-endpoint

通过 meta 标签实现

适用于无法修改服务器配置的情况:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">

通过报告模式实现

先监控不强制执行,适合调试阶段:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violation-report-endpoint

CSP 指令详解

常用指令

  • default-src: 默认策略,适用于未单独指定的指令
  • script-src: 控制 JavaScript 的加载源
  • style-src: 控制 CSS 的加载源
  • img-src: 控制图片的加载源
  • connect-src: 控制 XHR、WebSocket 等连接的源
  • font-src: 控制字体文件的加载源
  • object-src: 控制 <object><embed><applet> 等标签
  • media-src: 控制 <audio><video> 等媒体资源
  • frame-src: 控制 <iframe> 的加载源(已废弃,改用 child-src)
  • child-src: 控制子上下文(iframe、worker 等)
  • worker-src: 控制 worker、shared worker、service worker 的源

特殊值

  • 'none': 禁止任何资源
  • 'self': 只允许同源资源
  • 'unsafe-inline': 允许内联脚本/样式(降低安全性)
  • 'unsafe-eval': 允许动态代码执行(如 eval)
  • 'strict-dynamic': 信任由已执行脚本动态加载的脚本
  • 'nonce-<base64值>': 通过一次性随机数允许特定内联脚本
  • 'sha256-<hash值>': 通过哈希值允许特定内联脚本

CSP 实战配置示例

基础安全配置

Content-Security-Policy: 
  default-src 'none';
  script-src 'self' 'unsafe-inline' 'unsafe-eval';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  font-src 'self';
  connect-src 'self';
  form-action 'self';
  frame-ancestors 'none';
  base-uri 'self';
  report-uri /csp-report

严格配置(推荐)

Content-Security-Policy: 
  default-src 'none';
  script-src 'self' https://cdn.example.com;
  style-src 'self';
  img-src 'self' data: https://*.example-cdn.com;
  font-src 'self';
  connect-src 'self' https://api.example.com;
  form-action 'self';
  frame-ancestors 'none';
  base-uri 'self';
  report-uri /csp-report

允许 Google Analytics 的配置

Content-Security-Policy: 
  default-src 'self';
  script-src 'self' https://www.google-analytics.com 'unsafe-inline';
  img-src 'self' https://www.google-analytics.com;
  style-src 'self' 'unsafe-inline';
  connect-src 'self' https://www.google-analytics.com;
  report-uri /csp-report

处理内联脚本和样式

使用 nonce

服务器生成随机数并注入 CSP 头和脚本标签:

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
  // 这个脚本会被执行
</script>

<script>
  // 这个脚本会被阻止
</script>

使用哈希

计算脚本内容的 SHA 哈希并添加到 CSP:

Content-Security-Policy: script-src 'sha256-abc123...'
<script>
  // 内容必须完全匹配哈希计算时的内容
  alert('Hello, world.');
</script>

CSP 报告机制

违规报告配置

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

报告示例(POST 数据)

{
  "csp-report": {
    "document-uri": "https://example.com/page.html",
    "referrer": "https://example.com/",
    "violated-directive": "style-src 'self'",
    "effective-directive": "style-src",
    "original-policy": "default-src 'self'; style-src 'self'",
    "disposition": "enforce",
    "blocked-uri": "https://evil.com/malicious.css",
    "line-number": 10,
    "column-number": 20,
    "source-file": "https://example.com/page.html",
    "status-code": 200,
    "script-sample": ".evil { color: red; }"
  }
}

常见问题与解决方案

第三方集成问题

问题:使用第三方库(如 jQuery、Bootstrap)时 CSP 报错

解决方案

  1. 将库托管到自己的服务器并使用 'self'
  2. 添加 CDN 地址到白名单:
    script-src 'self' https://code.jquery.com
    
  3. 使用子资源完整性(SRI):
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"
            integrity="sha384-KyZXEAg3QhqLMpG8r+Knujsl5/1EN..."
            crossorigin="anonymous"></script>
    

内联事件处理器问题

问题onclick 等 HTML 事件处理器被阻止

解决方案

  1. 改用外部脚本添加事件监听器
    document.getElementById('myButton').addEventListener('click', function() {
      // 处理点击
    });
    
  2. 使用 'unsafe-inline'(不推荐)
  3. 使用 CSP Level 3 的 'unsafe-hashes'

动态样式问题

问题:JavaScript 动态修改样式被阻止

解决方案

  1. 改用 class 切换而非直接修改 style
    element.classList.add('active');
    
  2. 使用 'unsafe-inline' 或哈希/nonce

CSP 与现代前端框架

React 应用配置

React 需要 'unsafe-inline' 用于其事件系统:

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

Vue 应用配置

Vue 需要 'unsafe-eval' 用于模板编译:

Content-Security-Policy: 
  default-src 'self';
  script-src 'self' 'unsafe-eval';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;

Angular 应用配置

Angular 需要 'unsafe-eval' 用于 JIT 编译:

Content-Security-Policy: 
  default-src 'self';
  script-src 'self' 'unsafe-eval';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;

对于 AOT 编译的 Angular 应用,可以移除 'unsafe-eval'

CSP 部署策略

分阶段部署

  1. 报告阶段:使用 Content-Security-Policy-Report-Only 监控潜在问题
  2. 宽松策略:初始部署时允许必要的 'unsafe-inline''unsafe-eval'
  3. 严格策略:逐步移除不安全选项,使用 nonce/hash 替代
  4. 持续优化:根据报告不断调整策略

监控与维护

  1. 设置报告端点收集违规信息
  2. 定期审查 CSP 报告
  3. 随着应用更新调整 CSP 策略
  4. 使用自动化工具测试 CSP 有效性

CSP 高级技巧

使用 strict-dynamic

Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic'

允许由已信任脚本动态加载的脚本执行,适用于现代模块化应用。

多个 CSP 策略

可以组合多个 CSP 头,策略会叠加限制:

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

最终策略是 script-src 'self' https://cdn.example.com

预加载指令

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

控制 <link rel="preload"> 资源的来源。

CSP 与其他安全机制的协同

与 Subresource Integrity (SRI) 结合

<script src="https://example.com/script.js"
        integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
        crossorigin="anonymous"></script>

与 Feature Policy 结合

Content-Security-Policy: default-src 'self'
Feature-Policy: geolocation 'none'; microphone 'none'

与 Expect-CT 结合

Content-Security-Policy: default-src 'self'
Expect-CT: max-age=86400, enforce, report-uri="https://example.com/report"

CSP 的局限性

  1. 浏览器兼容性:不同浏览器对 CSP 的支持程度不同
  2. 配置复杂性:严格的 CSP 可能需要重构现有代码
  3. 误报问题:过于严格的策略可能阻止合法资源
  4. 性能影响:复杂的策略可能增加浏览器处理时间
  5. 不防所有攻击:不能防范 CSRF、SQL 注入等其他攻击

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌