以下是在 Nginx + Laravel 环境中配置 CSP (Content Security Policy) 安全头的完整指南,涵盖动态生成 Nonce、报告模式、以及常见框架兼容性处理:
1. CSP 核心概念
CSP 通过白名单控制允许加载的资源(JS/CSS/图片等),防止 XSS 和数据注入攻击。
关键指令:
default-src
:默认资源类型限制script-src
:控制 JavaScript 来源style-src
:控制 CSS 来源report-uri
:违规报告地址(可选)
2. 基础 Nginx 配置
在 Nginx 的 SSL 配置块(/etc/nginx/sites-available/laravel.conf
)中添加:
server {
listen 443 ssl;
# ... 其他 SSL 配置 ...
# 基础 CSP 策略(示例)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self';";
# 可选:启用 CSP 报告模式(不强制拦截,仅记录)
# add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-violation-report-endpoint";
}
3. Laravel 动态生成 Nonce(关键安全实践)
3.1 创建中间件
php artisan make:middleware ApplyCspHeaders
编辑 app/Http/Middleware/ApplyCspHeaders.php
:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Str;
class ApplyCspHeaders
{
public function handle($request, Closure $next)
{
$response = $next($request);
// 生成随机 Nonce
$nonce = Str::random(32);
$request->attributes->set('csp_nonce', $nonce);
// 设置 CSP 头(动态包含 Nonce)
$cspPolicy = "default-src 'self';" .
"script-src 'self' 'nonce-{$nonce}' https://cdn.jsdelivr.net;" .
"style-src 'self' 'nonce-{$nonce}';" .
"img-src 'self' data:;";
$response->header('Content-Security-Policy', $cspPolicy);
return $response;
}
}
3.2 注册中间件
在 app/Http/Kernel.php
的 $middleware
数组中添加:
protected $middleware = [
\App\Http\Middleware\ApplyCspHeaders::class,
// ... 其他中间件 ...
];
3.3 在 Blade 模板中使用 Nonce
<script nonce="{{ request()->attributes->get('csp_nonce') }}">
// 内联 JS 代码(需 Nonce 才能执行)
</script>
<link href="/style.css" rel="stylesheet" nonce="{{ request()->attributes->get('csp_nonce') }}">
4. 针对常见服务的 CSP 规则
4.1 允许 Google Analytics
add_header Content-Security-Policy "... script-src 'self' https://www.google-analytics.com; connect-src 'self' https://www.google-analytics.com;";
4.2 允许 YouTube 嵌入
add_header Content-Security-Policy "... frame-src 'self' https://www.youtube.com;";
4.3 允许 WebSocket 连接
add_header Content-Security-Policy "... connect-src 'self' wss://your-websocket.example.com;";
5. 报告模式与违规监控
5.1 Laravel 路由接收违规报告
// routes/api.php
Route::post('/csp-violation-report-endpoint', function (Request $request) {
Log::channel('security')->warning('CSP Violation:', $request->all());
return response()->noContent();
});
5.2 Nginx 配置报告地址
add_header Content-Security-Policy "... report-uri https://your-domain.com/csp-violation-report-endpoint;";
6. 严格模式配置(生产环境推荐)
add_header Content-Security-Policy "
default-src 'none';
script-src 'self' 'nonce-{NONCE}' 'strict-dynamic';
style-src 'self' 'nonce-{NONCE}';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
";
注意:
- 替换
{NONCE}
为 Laravel 动态生成的 Nonce 'strict-dynamic'
允许通过 JS 动态加载的脚本
7. 测试与验证
7.1 使用浏览器控制台
- 检查是否有
Content-Security-Policy
响应头 - 观察是否有 CSP 违规警告
7.2 自动化扫描工具
# 使用 Mozilla Observatory 测试
curl -X POST https://http-observatory.security.mozilla.org/api/v1/analyze?host=your-domain.com
8. 常见问题解决
问题 1:内联样式/脚本被阻止
- 方案:使用 Nonce 或 Hash 白名单
script-src 'self' 'sha256-abc123...';
问题 2:CDN 资源加载失败
- 方案:明确添加可信 CDN 到白名单
script-src 'self' https://cdn.jsdelivr.net;
问题 3:CSP 头被覆盖
- 检查:Nginx 中是否有多个
add_header
指令(后者会覆盖前者)
9. 安全增强建议
- 逐步启用:先使用
Content-Security-Policy-Report-Only
模式监控 - 最小权限:从
default-src 'none'
开始,逐步添加必要规则 - 自动化工具:
- Report URI 分析违规报告
- CSP Evaluator 检查策略有效性
最终配置示例
Nginx + Laravel 动态 Nonce 方案:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-{NONCE}' https://cdn.jsdelivivr.net; style-src 'self' 'nonce-{NONCE}'; img-src 'self' data:; report-uri /csp-violation-report-endpoint;";
Blade 模板:
<!-- 动态 Nonce 生效示例 -->
<script nonce="{{ request()->attributes->get('csp_nonce') }}">
console.log('This inline script is allowed!');
</script>
通过以上步骤,你的 Laravel 应用将获得企业级 CSP 防护,平衡安全性与功能性。