Skip to content

CSP(内容安全策略)构建

Livewire 提供了一个 CSP 安全构建,允许您在禁止 'unsafe-eval' 的严格内容安全策略(CSP)标头环境中使用 Livewire 应用程序。

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

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

为什么 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
<!-- 以下示例有效 -->
<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
<!-- 以下示例有效 -->
<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
<!-- 以下示例有效 -->
<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
<!-- 以下示例有效 -->
<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
<!-- ❌ 以下示例无效 -->
<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
<!-- ❌ 以下示例无效 -->
<div x-text="`Hello ${name}`">Bad</div>
<div x-data="{ ...defaults }">Bad</div>
<button x-on:click="() => doSomething()">Bad</button>

动态属性访问

html
<!-- ❌ 以下示例无效 -->
<div wire:show="user[dynamicProperty]">Bad</div>
<button wire:click="this[methodName]()">Bad</button>

解决限制的方法

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

html
<!-- 使用复杂内联表达式的替代方案 -->
<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 标头示例:

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 限制的平台