Laravel+Vue+Vite项目中为Vue注入的内联样式/脚本添加CSP Nonce
我有一个基于Vite的Laravel与Vue同仓库项目,已经通过spatie/laravel-csp实现了内容安全策略(CSP),浏览器检查显示script-src和style-src的nonce已成功生成,但出现以下两个错误:
the 'unsafe-inline' keyword, a hash ('sha256-nMxMqdZhkHxz5vAuW/PAoLvECzzsmeAxD/BNwG15HuA='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present.Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'nonce-H1R2LrVAcuLDIUMAB8wqwBzBFstVEppy'"
我推测是Vue注入的内联样式和脚本未携带nonce导致的问题,该如何解决?
以下是我的自定义Policy代码:
<?php namespace App\Support\Csp\Policies; use Spatie\Csp\Directive; use Spatie\Csp\Policies\Basic; class CustomPolicy extends Basic { public function configure() { parent::configure(); $this->addDirective(Directive::FONT, ['fonts.gstatic.com']) ->addDirective(Directive::STYLE, ['https://fonts.googleapis.com']) ->addDirective(Directive::IMG, ['data:']); } }
1. 解决内联脚本/样式的nonce缺失问题
Vue开发环境会动态注入内联脚本和样式,Vite的客户端入口脚本也得带nonce才能通过CSP校验。要让Laravel把生成的nonce传递给Vite,让它自动给注入的资源带上nonce:
第一步:在Blade模板里给Vite传nonce
修改主Blade视图(比如resources/views/app.blade.php),调用@vite时加上nonce参数:
<head> <!-- 其他头部内容 --> @vite(['resources/js/app.js'], ['nonce' => csp_nonce()]) </head>
csp_nonce()是spatie/laravel-csp提供的辅助函数,直接获取当前请求生成的nonce即可。
第二步:确保CSP的style-src包含nonce
你当前的Policy里style-src只添加了谷歌字体,要保证它包含自动生成的nonce。Basic策略默认已经包含nonce,但如果手动覆盖了style-src,需要显式添加:
$this->addDirective(Directive::STYLE, [ 'https://fonts.googleapis.com', "'nonce-" . csp_nonce() . "'" ]);
更省心的方式是直接用addDirective在原有基础上追加,因为Basic策略本身已经包含了'self'和nonce,无需手动添加,除非你清空了原有指令。
2. 解决unsafe-eval错误
Vue开发环境会用eval实现热更新和响应式调试,所以得在CSP的script-src里添加'unsafe-eval'。生产环境下Vue会编译成纯JS,不需要这个配置,所以按环境区分处理:
修改CustomPolicy:
public function configure() { parent::configure(); $this->addDirective(Directive::FONT, ['fonts.gstatic.com']) ->addDirective(Directive::STYLE, ['https://fonts.googleapis.com']) ->addDirective(Directive::IMG, ['data:']); // 仅开发环境添加unsafe-eval if (app()->environment('local')) { $this->addDirective(Directive::SCRIPT, "'unsafe-eval'"); } }
额外提醒
- 生产部署前确认Vite已经完成构建,这时Vue不会生成内联脚本/样式,也不需要
unsafe-eval,可以移除相关配置。 - 如果仍有内联样式报错,检查组件中是否使用了
style属性(内联样式),这种情况要么给style-src添加'unsafe-hashes'和对应的哈希值,要么改用CSS类控制样式。示例:
$this->addDirective(Directive::STYLE, [ 'https://fonts.googleapis.com', "'unsafe-hashes'", "'sha256-nMxMqdZhkHxz5vAuW/PAoLvECzzsmeAxD/BNwG15HuA='" ]);
优先推荐用CSS类替代内联样式。
内容的提问来源于stack exchange,提问作者Alphy Gacheru

