如何为Laravel应用配置内容安全策略(CSP)? (Spatie/laravel-csp包)

18次阅读

直接写 CSP 响应头易覆盖 laravel 默认安全头、难以动态控制 nonce/report-uri 且多中间件可能冲突;Spatie/laravel-csp 未生效需检查中间件注册位置、DEBUG 模式禁用及头覆写问题;内联脚本应优先用 nonce 或 hash 而非 unsafe_inline;connect-src 需单独配置外部 API;建议先用 Report-Only 模式收集日志再启用强制策略。

如何为Laravel应用配置内容安全策略(CSP)? (Spatie/laravel-csp包)

为什么直接写 Content-Security-Policy 响应头会出问题

手动在中间件或 apphttpKernel 里用 header()response()->withHeaders() 添加 CSP 头,容易覆盖 Laravel 自带的 X-Frame-OptionsX-Content-Type-Options 等安全头,也难以动态控制内联脚本、nonce、report-uri 等复杂策略。更关键的是:Laravel 的响应生命周期中,多个中间件可能重复设置同名头,导致策略被截断或解析失败。

Spatie/laravel-csp 注册中间件后没生效?检查这三点

安装后运行 php artisan vendor:publish --provider="SpatieCspCspServiceProvider" 生成配置文件 config/csp.php,但仅此不够:

  • 确认 app/Http/Kernel.php$middlewareGroups['web'] 中已加入 SpatieCspAddCspHeaders::class(不是 global middleware,否则 API 路由也会被强制加头)
  • 检查是否启用了 APP_DEBUG=true:该包默认在 debug 模式下禁用 CSP(避免开发时白屏),需显式设置 'enabled' => env('CSP_ENABLED', true) 并在 .env 中设 CSP_ENABLED=true
  • 确认没有其他中间件(如自定义安全头中间件、cdn 缓存中间件)在 AddCspHeaders 之后又覆写了 Content-Security-Policy

csp.php 配置里 'scripts' => []'unsafe_inline' 怎么选

允许内联脚本最常见需求是 vue/Alpine 的 @click 或 Blade 中的 @stack('scripts'),但 unsafe_inline 是高危选项,应优先用 nonce 或 hash:

  • 启用 nonce:在 config/csp.php 中设 'scripts' => ['self', 'unsafe_inline'] → 不安全;正确做法是删掉 unsafe_inline,改用 'scripts' => ['self', 'nonce-{{ csp_nonce() }}'],并在 Blade 模板中给 加上 nonce 属性
  • 用 hash 替代:对已知内联脚本内容做 SHA256,例如 对应 'sha256-abc123...',填入 'scripts' => ['self', 'sha256-abc123...'];但每次修改脚本都要重算 hash,适合静态片段
  • 第三方 js(如 google Analytics)必须显式添加域名:'scripts' => ['self', 'https://www.googletagmanager.com'],不能只写 googletagmanager.com(协议和端口必须匹配)

如何让 CSP 允许 fetch() 请求到外部 API(比如 Stripe)

CSP 的 connect-src 控制 fetchXMLHttpRequestEventSource 等连接行为,和 script-src 是分开的:

return [     'connect-src' => [         'self',         'https://api.stripe.com',         'https://hooks.stripe.com',     ], ];

注意:connect-src继承 script-src 的值;如果漏配,浏览器控制台会报 Refused to connect to 'https://api.stripe.com' because it violates the following Content Security Policy directive;另外,localhost 开发环境需单独加上 'http://localhost:8000'(协议不能省)。

CSP 的 report-urireport-to 在现代浏览器中行为不一致,且上报 endpoint 需要能接收 POST json 并防滥用,实际项目中建议先用 Content-Security-Policy-Report-Only 头跑一周收集违规日志,再切到 enforce 模式——这点很容易被跳过。

text=ZqhQzanResources