适用场景
CSP 内容安全策略配置适用于以下场景:网站存在 XSS 或前端注入风险,需要通过浏览器侧白名单做纵深防御;页面引入了第三方 CDN 脚本、统计代码等外部资源,需要精确控制加载来源;以及满足 PCI-DSS、等保 2.0 等合规要求中对 Web 应用安全响应头的检查项。CSP 是浏览器原生安全机制,即使应用层过滤被绕过,也能在浏览器端阻止恶意脚本执行,是 XSS 防护体系中性价比最高的一环。
前置条件
- 网站运行在 Nginx(推荐)或 Apache 上,具备修改虚拟主机配置的权限
- 已梳理页面实际加载的资源域名清单(脚本、样式、图片、接口地址)
- 测试环境与生产环境分离,便于灰度验证策略
原理说明
内容安全策略(Content Security Policy)通过 HTTP 响应头 Content-Security-Policy 告知浏览器:页面允许加载哪些来源的资源。其核心指令包括 default-src(兜底)、script-src(脚本)、style-src(样式)、img-src(图片)、connect-src(XHR/fetch)、frame-ancestors(嵌入来源,防点击劫持)等。当页面尝试加载白名单之外的资源时,浏览器直接拦截并在控制台输出违规报告。CSP 对内联脚本与 eval()默认全部禁止,如需放行,可通过 nonce(随机一次性令牌)或 hash(脚本内容哈希)精确授权,避免使用 'unsafe-inline' 削弱防护效果。
操作步骤
1. 梳理页面资源清单
用浏览器开发者工具 Network 面板收集页面全部请求,按类型归类域名,作为策略白名单依据:
# 用 curl 抓取页面并提取外部资源域名(示意)
curl -s https://example.com | grep -oE 'src="[^"]*' | cut -d'/' -f3 | sort -u
2. Nginx 配置基线策略
# 在 server 块中添加响应头
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';" always;
该基线策略只允许同源资源,是最安全的起点。保存后重载配置:
nginx -t && systemctl reload nginx
3. 按需放行外部资源
根据资源清单逐步追加域名白名单,例如:
add_header Content-Security-Policy "default-src 'self';
script-src 'self' https://cdn.example.com https://www.googletagmanager.com;
style-src 'self' https://cdn.example.com;
img-src 'self' data: https://img.example.com;
connect-src 'self' https://api.example.com;" always;
4. 用 nonce 放行内联脚本
页面中的关键内联脚本可用 nonce 精确放行:
# Nginx 生成随机 nonce 并注入响应头(配合 SSI 或 Lua 模块示例)
set $nonce $request_id;
add_header Content-Security-Policy "script-src 'self' 'nonce-$nonce';" always;
# 页面内联脚本标签携带相同 nonce 属性
<script nonce="{{nonce}}">console.log('ok');</script>
5. 灰度上线:先开报告模式
先用 Content-Security-Policy-Report-Only 只上报不拦截,观察真实页面是否存在漏配:
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report;" always;
# Nginx 接收违规上报的 location(示意)
location = /csp-report {
proxy_pass http://127.0.0.1:9000/csp; # 转发到日志采集服务
access_log /var/log/nginx/csp.log;
}
6. 生产启用并持续监控
# 确认报告模式无大量误报后,切换为强制执行
add_header Content-Security-Policy "...完整策略..." always;
# 定期分析违规上报日志,新增资源时同步更新白名单
grep -oE 'blocked-uri[^,]*' /var/log/nginx/csp.log | sort | uniq -c | sort -rn | head
配置验证
# 1. 确认响应头已正确下发
curl -sI https://example.com | grep -i content-security-policy
# 2. 检查页面功能回归:登录、支付、统计均正常
# 3. 浏览器控制台确认无 CSP 违规报错(F12 → Console)
# 4. 用安全检测工具验证策略强度(CSP Evaluator / Google 在线检测)
验证要点:响应头中存在 Content-Security-Policy;策略中不包含 'unsafe-inline' 与 'unsafe-eval'(特殊情况除外);控制台无 Refused to load 报错;业务核心链路功能完整。
常见问题
Q1:上线 CSP 后第三方统计/广告脚本被拦截,业务数据缺失怎么办?
分两类处理:若脚本来自固定域名,在 script-src 中追加该域名白名单;若脚本是内联注入或动态加载,改用 nonce 方案由后端为每个合法脚本标签生成随机令牌。切忌直接加 'unsafe-inline',那会让 CSP 对 XSS 几乎失效。上线前务必先在 Report-Only 模式下观察一周,统计全部被拦资源再定稿。
Q2:页面使用了大量内联样式和 eval,如何平滑迁移?
内联样式建议逐步外链化,或仅在 style-src 中保留 'unsafe-inline'(风险相对 script 低,仍可接受);eval 类代码(如旧版 webpack、Vue 2 开发模式)应优先升级框架,临时方案是允许 'unsafe-eval' 并限期整改。核心原则:script 严格、style 可宽、限期收敛,分阶段收紧策略,避免一次性全量拦截导致线上故障。
总结
CSP 内容安全策略是浏览器侧的“最后一道防线”,能以极低成本阻断绝大多数 XSS 与数据注入攻击。正确的落地路径是:梳理资源 → 基线白名单 → 报告模式灰度 → 强制执行 → 持续监控。配合 nonce 机制放行必要内联脚本,可在安全与业务可用性之间取得平衡。建议将 CSP 检查纳入 CI 流水线,新功能上线即校验响应头完整性,防止策略被误删或弱化。