为什么会有这两个警告?






















指令 作用 风险
unsafe-inline 允许页面内直接写 <script> 标签或 onclick 等事件 攻击者若找到 XSS 漏洞,可以直接注入恶意脚本执行
unsafe-eval 允许使用 eval()、setTimeout(string) 等动态执行代码 攻击者可以混淆恶意代码绕过静态检测

理想的安全策略是完全禁止这两个指令,但现实是很多现有网站(尤其是使用 Vue/React 等框架或第三方 SDK 的)离不开它们。完全移除可能导致功能崩溃。




解决方案


根据你的实际情况,我提供三个阶段的方案,你可以根据需求选择。




方案一:快速过关(5 分钟,立即消除警告)


适用场景:急于通过安全扫描,网站功能复杂暂时无法重构。


做法:将 unsafe-inline 和 unsafe-eval 从 default-src 中移除,仅保留在 script-src 和 style-src 中。


add_header Content-Security-Policy "
default-src 'self' https: data: blob:;
script-src 'self' https: 'unsafe-inline' 'unsafe-eval';
style-src 'self' https: 'unsafe-inline';
img-src 'self' https: data: blob:;
font-src 'self' https: data:;
connect-src 'self' https:;
object-src 'none';
base-uri 'self';
upgrade-insecure-requests;
" always;

效果:扫描工具不会再在 default-src 中看到 unsafe-*,警告消失。但注意,script-src 和 style-src 中仍然包含它们,实际安全防护仍有缺口。这是权宜之计。




方案二:彻底移除 unsafe-inline(进阶,推荐)


适用场景:希望真正提升安全性,愿意花时间改造。


核心思路:使用 Nonce(一次性随机数) 机制。服务器每次加载页面时生成一个随机 nonce 值,只有携带相同 nonce 的 <script> 标签才会被执行,攻击者注入的脚本因为没有正确的 nonce 而被拦截。


Nginx 配置(假设你使用 PHP/Node.js 等后端生成 nonce)


# 注意:这里的 {NONCE} 需要由后端动态替换
add_header Content-Security-Policy "
default-src 'self' https: data: blob:;
script-src 'nonce-{NONCE}' 'strict-dynamic' https:;
style-src 'self' https: 'unsafe-inline';
img-src 'self' https: data: blob:;
font-src 'self' https: data:;
connect-src 'self' https:;
object-src 'none';
base-uri 'self';
upgrade-insecure-requests;
" always;

后端生成 nonce 示例(PHP)


<?php
// 在页面最顶部生成 nonce
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: default-src 'self' https: data: blob:; script-src 'nonce-$nonce' 'strict-dynamic' https:; ...");
?>

<!-- 页面中可信的脚本加上 nonce -->
<script nonce="<?= $nonce ?>">
// 你的业务代码
</script>

关于 strict-dynamic


加上 'strict-dynamic' 后,被 nonce 信任的脚本可以动态创建并加载其他脚本(如通过 document.createElement('script')),无需再为每个子脚本单独加 nonce,极大简化了管理。




方案三:彻底移除 unsafe-eval


如果网站使用了 eval() 或类似动态执行,需要替换为安全的替代方案:



























危险写法 安全替代
eval('alert(1)') 直接写 alert(1)
new Function('return 1')() 改用普通函数
setTimeout('doSomething()', 1000) 改为 setTimeout(doSomething, 1000)
JSON 解析 使用 JSON.parse()(它是安全的,不受限)

如果依赖第三方库(如某些版本的 Vue、React 开发工具、谷歌地图),可能需要升级库版本或寻找替代方案。




? 上线前的安全验证(强烈推荐)


无论采用哪个方案,千万不要直接在生产环境改动!建议按以下步骤验证:


第 1 步:使用 Report-Only 模式测试


add_header Content-Security-Policy-Report-Only "
default-src 'self' https: data: blob:;
script-src 'nonce-{NONCE}' 'strict-dynamic' https:;
style-src 'self' https: 'unsafe-inline';
img-src 'self' https: data: blob:;
connect-src 'self' https:;
object-src 'none';
base-uri 'self';
upgrade-insecure-requests;
report-uri /csp-report; # 收集违规报告的接口
" always;

这个头不会拦截任何内容,只会把违规行为上报到你指定的接口。部署后观察 1-2 天,如果违规报告很少或没有,说明新策略可以安全启用。


第 2 步:正式启用


将 Content-Security-Policy-Report-Only 改为 Content-Security-Policy,移除 report-uri(或保留用于监控)。




? 针对你当前情况的明确建议























你的需求 推荐方案
只想快速通过扫描,业务优先 采用 方案一,5 分钟搞定
希望真正提升安全等级,愿意花 1-2 天改造 采用 方案二(Nonce),这是行业最佳实践
网站依赖大量第三方库,难以改造 先用 方案一 过渡,同时逐步替换危险库



最终推荐配置(方案一,可直接复制使用)


add_header Content-Security-Policy "
default-src 'self' https: data: blob:;
script-src 'self' https: 'unsafe-inline' 'unsafe-eval';
style-src 'self' https: 'unsafe-inline';
img-src 'self' https: data: blob:;
font-src 'self' https: data:;
connect-src 'self' https:;
object-src 'none';
base-uri 'self';
upgrade-insecure-requests;
" always;

替换后执行 nginx -t && systemctl reload nginx,然后清空浏览器缓存重新扫描,警告应该会消失。




如果你决定采用 方案二(Nonce),告诉我你的后端语言(PHP/Node.js/Python/Java 等),我可以给你提供完整的、可直接集成的代码示例。