CSP内容安全策略:深度防御配置

作者:Yolo 发布时间: 2026-06-14 阅读量:10

CSP内容安全策略:深度防御配置

CSP(Content Security Policy,内容安全策略)是浏览器层面的一道安全防线,用来限制页面能加载哪些资源、执行哪些脚本。如果说同源策略划定了"谁能访问我",那CSP就是"我能加载什么"。配置得当,它能有效缓解XSS、数据注入等攻击;配置失误,轻则策略失效,重则直接阻断正常功能。

一、CSP是什么,解决什么问题

CSP通过HTTP响应头Content-Security-Policy或HTML的<meta>标签声明,告诉浏览器:这个页面只允许从特定来源加载脚本、样式、图片、字体等资源,禁止内联脚本和eval等危险操作。

核心目标:

  • 阻止XSS:即使攻击者注入脚本,浏览器也不会执行
  • 限制资源加载:防止恶意资源(图片、iframe、媒体)被加载
  • 强制HTTPS:通过upgrade-insecure-requests自动升级HTTP请求
  • 报告违规:通过report-urireport-to收集策略违反日志
  • 二、CSP指令速查表

    | 指令 | 作用 | 示例 | |---|---|---| | default-src | 默认资源策略,其他指令未设置时生效 | 'self' | | script-src | JavaScript来源 | 'self' https://cdn.example.com | | style-src | CSS来源 | 'self' 'unsafe-inline' | | img-src | 图片来源 | 'self' data: https: | | font-src | 字体来源 | 'self' https://fonts.gstatic.com | | connect-src | XHR/WebSocket/Worker | 'self' | | frame-src | iframe来源 | 'none' | | media-src | 音视频来源 | 'self' | | object-src | Flash/插件 | 'none' | | base-uri | <base>标签允许的值 | 'self' | | form-action | 表单提交目标 | 'self' | | frame-ancestors | 谁可以嵌入本页(防点击劫持) | 'none' | | upgrade-insecure-requests | HTTP自动升级HTTPS | 无值 | | block-all-mixed-content | 阻止混合内容 | 无值 |

    关键字

  • 'none':完全禁止
  • 'self':同源(同协议+域名+端口)
  • 'unsafe-inline':允许内联脚本/样式(削弱安全性
  • 'unsafe-eval':允许eval()new Function()削弱安全性
  • 'strict-dynamic':允许由nonce/hash标记的脚本动态加载子脚本(推荐配合nonce使用)
  • 'nonce-<base64>':仅允许带有匹配nonce的内联脚本
  • 'sha256-<base64>':仅允许匹配哈希的内联脚本
  • 三、配置示例:从宽松到严格

    1. 最基础的防护

    Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;
    
  • 所有资源默认只能同源加载
  • 样式允许内联(因为很多UI框架需要)
  • 图片允许同源、data URI和HTTPS
  • 2. 防御XSS的推荐配置

    Content-Security-Policy: 
      default-src 'self';
      script-src 'self' 'nonce-random123' 'strict-dynamic';
      style-src 'self' 'nonce-random123';
      img-src 'self' data: https:;
      connect-src 'self';
      frame-src 'none';
      object-src 'none';
      base-uri 'self';
      form-action 'self';
      frame-ancestors 'none';
      upgrade-insecure-requests;
    
    要点:
  • script-src使用nonce + strict-dynamic,允许受信任脚本动态加载子脚本
  • 完全禁止iframe嵌入和插件
  • 防止点击劫持:frame-ancestors 'none'
  • 自动升级HTTP请求到HTTPS
  • 3. 仅报告模式(Report-Only)

    Content-Security-Policy-Report-Only: default-src 'self'; report-uri https://report.example.com/csp;
    
    不拦截任何请求,只收集违规报告。适合逐步上线CSP时先用它观察影响范围。

    4. 现代报告收集(Reporting API)

    Content-Security-Policy: default-src 'self'; report-to csp-endpoint;
    Reporting-Endpoints: csp-endpoint="https://report.example.com/csp"
    
    report-uri已废弃,新版浏览器使用report-to配合Reporting-Endpoints头。

    四、Nonce与Hash:内联脚本的出路

    Nonce方式(推荐)

    服务器生成随机nonce,每请求唯一:
    Content-Security-Policy: script-src 'nonce-abc123';
    
    <script nonce="abc123">
      console.log('这个脚本被允许执行');
    </script>
    
    攻击者无法预测nonce,因此注入的脚本不会执行。

    Hash方式

    计算内联脚本的SHA256哈希:
    Content-Security-Policy: script-src 'sha256-abc123...';
    
    适合内容完全静态的内联脚本。内容一变哈希就失效,维护成本高。

    五、CSP的常见绕过与陷阱

    1. unsafe-inlineunsafe-eval的滥用

    Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval';
    
    这基本等于没开CSP。很多框架(如Vue开发模式)默认需要unsafe-eval,但生产环境必须移除。

    2. 通配符和宽松域名

    Content-Security-Policy: script-src *;
    Content-Security-Policy: script-src https:;
    Content-Security-Policy: script-src 'self' https://*.google.com;
    
    *https:允许任何来源的脚本,攻击者只需上传脚本到任意HTTPS站点即可绕过。*.google.com包含用户可上传内容的服务(如Google Sites、Firebase Hosting)。

    3. JSONP和Angular回调

    如果策略允许https://api.example.com,而该域名提供JSONP端点:
    <script src="https://api.example.com/data?callback=alert(1)"></script>
    
    攻击者可以利用JSONP回调执行任意代码。策略中应避免允许提供JSONP的域名,或配合script-src的严格限制。

    4. data: URI的风险

    Content-Security-Policy: script-src 'self' data:;
    
    <script src="data:text/javascript,alert(1)"></script>
    
    允许data:意味着任何人可以构造内联脚本。

    5. javascript:伪协议

    <a href="javascript:alert(1)">click</a>
    
    即使script-src很严格,javascript:伪协议在部分浏览器仍可能执行。需要配合default-src或明确禁止。

    六、实战:为现代Web应用配置CSP

    React/Vue应用

    Content-Security-Policy: 
      default-src 'self';
      script-src 'self' 'nonce-{nonce}' 'strict-dynamic';
      style-src 'self' 'nonce-{nonce}' 'unsafe-inline';
      img-src 'self' data: https: blob:;
      font-src 'self' https://fonts.gstatic.com;
      connect-src 'self' https://api.example.com;
      frame-src 'none';
      object-src 'none';
      base-uri 'self';
      form-action 'self';
      frame-ancestors 'none';
      upgrade-insecure-requests;
    
    注意:
  • 开发模式可能需要unsafe-eval,生产环境必须去掉
  • 如果使用Webpack的style-loader,需要unsafe-inline或改用MiniCssExtractPlugin
  • 使用Google Fonts

    Content-Security-Policy: 
      style-src 'self' https://fonts.googleapis.com;
      font-src 'self' https://fonts.gstatic.com;
    

    使用Google Analytics / Tag Manager

    Content-Security-Policy: 
      script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
      img-src 'self' https://www.google-analytics.com;
      connect-src 'self' https://www.google-analytics.com;
    

    七、CSP的局限与补充

    CSP不是银弹:

  • DOM-based XSS:如果应用本身通过innerHTML、DOM操作引入恶意内容,CSP无法阻止
  • 存储型XSS:如果攻击者上传的图片、PDF等文件被直接访问,CSP不保护
  • 浏览器兼容性:旧版浏览器不支持,需要其他防御层
  • 配置复杂:大型应用资源来源多,策略维护成本高
  • CSP应该作为纵深防御的一部分,配合输入过滤、输出编码、同源策略、WAF等共同使用。

    八、检测与调试

    浏览器DevTools

    Chrome/Firefox的Network面板查看CSP头,Console面板查看违规报告:
    [Report Only] Refused to load the script 'https://evil.com/xss.js' because it violates the following Content Security Policy directive: "script-src 'self'".
    

    在线验证工具

  • [Google CSP Evaluator](https://csp-evaluator.withgoogle.com/)
  • [CSP Scanner](https://cspscanner.com/)
  • 测试报告收集

    // 监听securitypolicyviolation事件
    document.addEventListener('securitypolicyviolation', (e) => {
      console.log('CSP违规:', {
        blockedURI: e.blockedURI,
        violatedDirective: e.violatedDirective,
        originalPolicy: e.originalPolicy
      });
    });
    

    总结

    CSP是浏览器送给Web开发者的安全礼物,但这份礼物需要正确拆开:

  • Report-Only开始,逐步收紧
  • 优先使用nonce + strict-dynamic,避免unsafe-inline
  • 域名白名单要精确,避免通配符和JSONP端点
  • 配合其他防御措施,不要单独依赖CSP
  • 配置得当的CSP,能让XSS攻击从"致命"降级为" nuisance"。