Skip to content

CSP 内容安全策略

Content Security Policy(CSP,内容安全策略)是 HTTP 安全头中最重要的一个,它通过白名单机制控制页面可以加载哪些资源、执行哪些脚本,从根源上防御 XSS 攻击。本节将详细介绍 CSP header 的配置方法、常用指令(default-src、script-src、style-src)、report-uri 报告机制。

前置知识

阅读本节前,建议先了解:XSS 防护

CSP 概述

CSP 通过指定哪些来源的资源可以被加载,有效防止 XSS 和数据注入攻击:

Content-Security-Policy: 指令 值; 指令 值;

基本指令

default-src

default-src 是所有资源类型指令的默认回退:

Content-Security-Policy: default-src 'self'
源值说明
'self'仅允许同源资源
'none'禁止所有来源
https:允许所有 HTTPS 资源
data:允许 data: URI
blob:允许 blob: URI
*.example.com允许指定域名的子域名

常用指令

指令控制资源
default-src默认(所有类型)
script-srcJavaScript
style-srcCSS 样式
img-src图片
font-src字体
connect-srcAJAX/WebSocket
media-src音频/视频
frame-srciframe
object-src插件(Flash 等)
manifest-srcWeb App Manifest
worker-srcWeb Worker
base-uri<base> 标签
form-action表单提交目标

配置示例

严格策略

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  style-src 'self';
  img-src 'self' data:;
  font-src 'self';
  connect-src 'self';
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';

允许 CDN

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  style-src 'self' https://cdn.example.com;
  img-src 'self' https://cdn.example.com data:;
  font-src 'self' https://fonts.googleapis.com https://fonts.gstatic.com;
  connect-src 'self' https://api.example.com;

使用 nonce

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>CSP nonce 示例</title>
  <!-- 使用 nonce 允许内联脚本 -->
  <meta http-equiv="Content-Security-Policy" content="
    default-src 'self';
    script-src 'self' 'nonce-a1b2c3d4';
    style-src 'self' 'unsafe-inline';
  ">
</head>
<body>
  <!-- 使用 nonce 的内联脚本 -->
  <script nonce="a1b2c3d4">
    // 这个脚本被 CSP 允许
    console.log('Hello from CSP!');
  </script>

  <!-- 外部脚本 -->
  <script src="https://cdn.example.com/lib.js" nonce="a1b2c3d4"></script>
</body>
</html>

使用 hash

<!-- 使用 hash 允许特定脚本 -->
Content-Security-Policy:
  script-src 'self' 'sha256-base64-encoded-hash'

<!-- hash 值通过以下命令生成 -->
<!-- echo -n "console.log('hello')" | openssl dgst -sha256 -binary | openssl base64 -->

report-uri 和 report-to

report-uri(已弃用但仍广泛支持)

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  report-uri /csp-violation-report-endpoint/;

report-to(新标准)

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  report-to csp-violations;

Report-To: {
  "group": "csp-violations",
  "max_age": 31536000,
  "endpoints": [
    { "url": "https://report.example.com/csp-reports" }
  ]
}

报告模式(仅报告)

使用 Content-Security-Policy-Report-Only 在不阻止违规的情况下收集报告:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https: 'unsafe-inline' 'unsafe-eval';
  report-uri /csp-report;

渐进式部署

建议先使用 Report-Only 模式运行一段时间,确认无误后再切换到正式 CSP。

报告处理

html
<!-- 服务器端接收 CSP 报告 -->
<!-- POST /csp-report -->
<!-- 报告格式为 JSON -->
<!--
{
  "csp-report": {
    "document-uri": "https://example.com/page.html",
    "violated-directive": "script-src 'self'",
    "blocked-uri": "https://evil.com/malicious.js",
    "source-file": "https://example.com/page.html",
    "line-number": 42,
    "column-number": 15
  }
}
-->

注意事项

  1. 从宽松策略开始:先使用 Report-Only 模式测试
  2. 'unsafe-inline' 和 'unsafe-eval' 应避免:它们削弱了 CSP 的防护
  3. nonce 每次请求都应不同:防止 nonce 被重放攻击
  4. CSP 不替代输入验证:CSP 是纵深防御的一部分
  5. 考虑第三方脚本:CDN、分析工具、广告等都需要在 CSP 中声明

最佳实践

  • 配置 Content-Security-Policy HTTP 头
  • 避免使用 'unsafe-inline' 和 'unsafe-eval'
  • 使用 nonce 或 hash 允许特定内联脚本
  • 先用 Report-Only 模式测试
  • 设置 frame-ancestors 'none' 防止点击劫持
  • 定期审查 CSP 报告
  • 设置合理的升级策略

下一节

继续学习:iframe 安全

参考链接