Skip to content
全部文档

CSP(内容安全策略)构建

Livewire 提供 CSP 安全构建,让你能在禁止 'unsafe-eval' 的严格内容安全策略(CSP)环境下使用 Livewire 应用。

什么是内容安全策略(CSP)?

内容安全策略(CSP)是一种安全标准,有助于防止各类攻击,包括跨站脚本(XSS)和代码注入。CSP 的工作方式是让 Web 开发者控制浏览器允许加载和执行哪些资源。

最严格的 CSP 指令之一是 'unsafe-eval':若省略它,就会阻止 JavaScript 通过 eval()new Function() 等在运行时把字符串编译并执行为代码的方式执行动态代码。

为什么 CSP 会影响 Livewire

默认情况下,Livewire(及其底层的 Alpine.js 框架)会使用 new Function() 来编译并执行来自 HTML 属性的 JavaScript 表达式,例如:

html
<button wire:click="$set('count', count + 1)">Increment</button>
<div wire:show="user.role === 'admin'">Admin panel</div>

虽然这种方式比直接使用 eval() 更快、更安全,但仍会违反许多注重安全的应用所强制执行的 'unsafe-eval' CSP 指令。

启用 CSP 安全模式

要启用 Livewire 的 CSP 安全模式,需要修改应用配置:

配置

config/livewire.php 文件中,将 csp_safe 选项设为 true

php
'csp_safe' => true,

对 Alpine.js 的影响

重要:在 Livewire 中启用 CSP 安全模式后,也会影响应用中的所有 Alpine.js 功能。Alpine 会自动使用其 CSP 安全求值器,这意味着应用中所有 Alpine 表达式都会受到相同的解析限制。

多数开发者会在这里感受到限制,因为 Alpine 表达式通常比典型的 Livewire 表达式更复杂。

支持哪些内容

CSP 构建支持你在 Livewire 中常用的大多数 JavaScript 表达式:

基本 Livewire 表达式

html
<!--  These work -->
<button wire:click="increment">+</button>
<button wire:click="decrement">-</button>
<button wire:click="reset">Reset</button>
<button wire:click="save">Save</button>
<input wire:model="name">
<input wire:model.live="search">

带参数的方法调用

html
<!--  These work -->
<button wire:click="updateUser('John', 25)">Update User</button>
<button wire:click="setCount(42)">Set Count</button>
<button wire:click="saveData({ name: 'John', age: 30 })">Save Object</button>

属性访问与更新

html
<!--  These work -->
<input wire:model="user.name">
<input wire:model="settings.theme">
<button wire:click="$set('user.active', true)">Activate</button>
<div wire:show="user.role === 'admin'">Admin Panel</div>

Alpine 中的基本表达式

html
<!--  These work -->
<div x-data="{ count: 0, name: 'Livewire' }" wire:ignore>
    <button x-on:click="count++">Increment</button>
    <span x-text="count"></span>
    <span x-text="'Hello ' + name"></span>
    <div x-show="count > 5">Count is high!</div>
</div>

不支持哪些内容

某些高级 JavaScript 特性在 CSP 安全模式下无法使用:

复杂 JavaScript 表达式

html
<!-- L These don't work -->
<button wire:click="items.filter(i => i.active).length">Count Active</button>
<div wire:show="users.some(u => u.role === 'admin')">Has Admin</div>
<button wire:click="(() => console.log('Hi'))()">Complex Function</button>

模板字符串与高级语法

html
<!-- L These don't work -->
<div x-text="`Hello ${name}`">Bad</div>
<div x-data="{ ...defaults }">Bad</div>
<button x-on:click="() => doSomething()">Bad</button>

动态属性访问

html
<!-- L These don't work -->
<div wire:show="user[dynamicProperty]">Bad</div>
<button wire:click="this[methodName]()">Bad</button>

绕过限制

对于复杂的 Alpine 表达式,请使用 Alpine.data(),或把逻辑移到方法中:

html
<!-- Instead of complex inline expressions -->
<div x-data="users">
    <div x-show="hasActiveAdmins">Admin panel available</div>
    <span x-text="activeUserCount">0</span>
</div>

<script nonce="[nonce]">
    Alpine.data('users', () => ({
        users: ...,

        get hasActiveAdmins() {
            return this.users.filter(u => u.active && u.role === 'admin').length > 0;
        },

        get activeUserCount() {
            return this.users.filter(u => u.active).length;
        }
    }));
</script>

CSP 响应头示例

下面是可与 Livewire CSP 安全构建配合使用的 CSP 响应头示例:

text
Content-Security-Policy: default-src 'self';
                        script-src 'nonce-[random]' 'strict-dynamic';
                        style-src 'self' 'unsafe-inline';

要点:

  • 从 `script-src` 指令中移除 `'unsafe-eval'`
  • 使用基于 nonce 的脚本加载,配合 `'nonce-[random]'`
  • 可考虑添加 `'strict-dynamic'`,以更好兼容动态加载的脚本

性能考量

CSP 安全构建使用不同的表达式求值器,其特点是:

  • **解析**:表达式的初次解析稍慢(通常可忽略)
  • **运行时**:简单表达式的运行时性能相近
  • **包体积**:因自定义解析器,JavaScript 包体积略大

对大多数应用来说,这些差异不易察觉,但仍建议结合你的具体场景进行测试。

测试你的 CSP 实现

要验证 CSP 配置是否生效:

  1. 在 Web 服务器或应用中**启用 CSP 响应头**
  2. 在**浏览器开发者工具中测试**——CSP 违规会出现在控制台
  3. **确认表达式可用**——所有 Livewire 与 Alpine 表达式应能正常工作
  4. **检查控制台错误**——不应出现 `unsafe-eval` 违规

何时使用 CSP 安全模式

在以下情况可考虑使用 CSP 安全模式:

  • 应用需要严格遵守 CSP
  • 你在为对安全敏感的环境构建应用
  • 组织的安全策略禁止 `'unsafe-eval'`
  • 你部署到强制要求 CSP 限制的平台