内容安全策略 (CSP) 是防御跨站脚本 (XSS) 攻击最有效的手段之一。通过告诉浏览器哪些资源可以加载,CSP 即使在应用存在漏洞时也能阻止恶意代码注入。
什么是 CSP,为什么重要?
CSP 是一个 HTTP 响应头,控制浏览器可以为给定页面加载哪些资源。没有 CSP,一个 XSS 漏洞就可能让攻击者注入脚本、窃取 Cookie、重定向用户或篡改网站。
CSP 工作原理
当浏览器收到 CSP 头时,会通过阻止违规资源来执行策略:
Browser receives HTTP response:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Browser allows:
✅ <script src="/app.js"> (same origin = 'self')
✅ <script src="https://cdn.example.com/lib.js">
Browser blocks:
❌ <script src="https://evil.com/steal.js">
❌ <script>alert('XSS')</script> (inline = not allowed)所有 CSP 指令
| 指令 | 控制 | 示例 |
|---|---|---|
| default-src | Fallback for all resource types | 'self' |
| script-src | JavaScript files and inline scripts | 'self' https://cdn.example.com |
| style-src | CSS stylesheets and inline styles | 'self' 'unsafe-inline' |
| img-src | Images (img, favicon, CSS background) | 'self' data: https: |
| font-src | Web fonts (@font-face) | 'self' https://fonts.gstatic.com |
| connect-src | XHR, fetch, WebSocket, EventSource | 'self' https://api.example.com |
| media-src | Audio and video elements | 'self' |
| frame-src | Iframes and embedded frames | 'self' https://www.youtube.com |
| object-src | Plugins (Flash, Java applets) | 'none' |
| base-uri | Base element URLs | 'self' |
| form-action | Form submission targets | 'self' |
| frame-ancestors | Who can embed this page (anti-clickjacking) | 'self' |
| upgrade-insecure-requests | Upgrade HTTP requests to HTTPS | (no value needed) |
来源值说明
| 来源 | 含义 |
|---|---|
| 'self' | Same origin (protocol + host + port) |
| 'none' | Block all resources of this type |
| 'unsafe-inline' | Allow inline scripts/styles (reduces security!) |
| 'unsafe-eval' | Allow eval(), Function(), setTimeout(string) |
| 'strict-dynamic' | Trust scripts loaded by already-trusted scripts |
| 'nonce-abc123' | Allow specific inline script with matching nonce attribute |
| 'sha256-...' | Allow inline script matching this hash |
| https: | Allow any HTTPS resource |
| data: | Allow data: URIs (e.g., inline images) |
| blob: | Allow blob: URIs |
| *.example.com | Allow all subdomains of example.com |
分步实施
步骤 1:从仅报告模式开始
先以仅报告模式部署 CSP,在不破坏网站的情况下识别违规:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report步骤 3:构建策略
根据分析结果创建策略。从严格开始,按需放宽:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests常见 CSP 配置
严格策略(最高安全性)
Content-Security-Policy:
default-src 'none';
script-src 'self';
style-src 'self';
img-src 'self';
font-src 'self';
connect-src 'self';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
object-src 'none';
upgrade-insecure-requests中等(典型 SPA)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com wss://ws.example.com;
frame-ancestors 'self'带 Google Analytics
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com https://analytics.google.com实现方法
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self'"Express.js
// Using helmet middleware (recommended)
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
}
}));
// Or manually
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'");
next();
});HTML Meta 标签
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">常见 CSP 错误
| 错误 | 影响 | 修复 |
|---|---|---|
| 脚本使用 unsafe-inline | 破坏 XSS 防护 | 改用 nonce 或 hash |
| 使用 unsafe-eval | 允许 eval() 等函数 | 重构代码避免 eval() |
| script-src 使用通配符 (*) | 允许任何域的脚本 | 白名单指定域名 |
| 忘记处理内联样式 | 阻止 CSS-in-JS 库 | style-src 添加 unsafe-inline 或使用 nonce |
| 未先在仅报告模式测试 | 破坏网站功能 | 始终先从仅报告模式开始 |
| 缺少 base-uri 指令 | 允许 base 标签注入 | 添加 base-uri 'self' |
常见问题
CSP 能防止哪些攻击?
CSP 主要通过控制脚本执行来防止 XSS 攻击。还能防止点击劫持(frame-ancestors)、数据注入和混合内容问题。
CSP 能替代输入验证吗?
不能。CSP 是深度防御措施。仍需验证和清理所有用户输入。CSP 是其他防御失败时的安全网。
如何在内联脚本中使用 CSP?
使用 nonce(添加到 CSP 头和 script 标签的随机令牌)或 hash(脚本内容的 SHA-256)。避免使用 unsafe-inline。
CSP 会拖慢网站吗?
不会。CSP 由浏览器执行,性能影响可以忽略。头本身很小。唯一的开销是生成 nonce(如果使用的话),这也是极小的。