一句话收获
Svelte 用编译时转义替代了运行时 sanitization,这让大多数 XSS 在开发阶段就被消除——但 {@html}、动态事件绑定和 SSR 上下文依然是开发者必须手动防御的边界。
风险描述:编译型框架“默认安全”的迷思
Svelte 官方 slogan 是“为其余的人准备的 Web 开发”,它的编译型思路让很多人以为安全完全由框架兜底。实际上,Svelte 在编译阶段确实比 React/Vue 多做了两件事:
- 将模板中的变量插值自动转义为文本节点,而非 innerHTML。
- 静态分析绑定,阻断部分运行时注入路径。
但这不代表开发者可以无脑插入用户输入。攻击者仍然可以利用 {@html}、事件属性拼接、以及 SSR 时的 JSON 序列化漏洞,绕过默认防护。

图:Svelte 模板中显式不安全标记(红色)和默认安全标记(绿色)的攻防路径。
漏洞原理分析:编译时安全如何工作
传统框架(如 React、Vue)在运行时通过虚拟 DOM diff 时对字符串进行转义(或调用 DOMPurify)。Svelte 跳过运行时直接生成原生 DOM 操作代码:
<script>
let userInput = '<img src=x onerror=alert(1)>';
</script>
<p>{userInput}</p>
编译后会变成类似:
function update($$) {
$$.p(…, /* textContent */) // 直接设置 textContent
}
攻击脚本不会被执行为 HTML。这比 React 的 dangerouslySetInnerHTML 更严:React 必须显式写 dangerouslySetInnerHTML,但 Svelte 默认就是 textContent。
但问题出在显式逃逸:{@html userInput} 直接对应 element.innerHTML = userInput,攻击者能完全控制 DOM。
真实案例 / PoC:一个被忽略的 SSR 侧信道
2023 年,SvelteKit(Svelte 的服务端渲染框架)曾曝出 CVE-2023-48268:SSR 返回的 HTML 中,如果模板包含用户控制的变量且未正确转义,可能导致 XSS。修复方案是编译时对 {@html} 的输入做更严格的校验。
PoC 思路(仅用于理解,勿实战):
<script context="module">
export const load = () => ({
props: { name: '<script>alert(1)</script>' }
});
</script>
<h1>Hello {@html name}</h1>
SvelteKit 在 SSR 阶段会直接输出 <script> 标签。虽然现代浏览器 CSP 可能阻止,但在无 CSP 或 report-only 模式下仍然有危害。
这告诉我们:框架的编译时安全只覆盖“默认插值”,{@html} 是开发者主动放弃安全的开关。
防护方案:开发者可以做什么
1. 永远不要用 {@html} 渲染用户输入
哪怕你认为已经用第三方库过滤了,也建议封装一个带消毒组件的专用组件:
<script>
import DOMPurify from 'dompurify';
export let html;
</script>
{@html DOMPurify.sanitize(html)}
2. 事件处理中的动态属性绑定
Svelte 允许动态事件处理:on:${eventName}。如果 eventName 来自用户输入,攻击者可以注入 click 变成 onerror 等不合法事件。始终将事件名做白名单校验。
3. SSR 时的 JSON 序列化
使用 fetch 或 load 函数传递数据给客户端时,确保 JSON.stringify 的参数中没有未转义的用户数据。SvelteKit 5 引入了 +page.js 的 serialize 方法,可以显式控制序列化。
4. CSP 作为第二道防线
即使 {@html} 被滥用,Content-Security-Policy 也能阻止内联脚本。建议将 script-src 设为 strict-dynamic 或 nonce。
安全加固清单(Svelte 项目专用)
| 检查点 | 操作 | 优先级 |
|---|---|---|
禁止 {@html} 直接绑定用户输入 |
用 DOMPurify 包装 | 高 |
| 动态事件名白名单 | 只允许已知事件(如 click/input) |
高 |
| SSR 返回的用户数据 | 确保两次序列化(JSON → 模板插值)时转义 | 中 |
| 第三方 Svelte 组件 | 检查组件是否暴露 {@html} 而不加消毒 |
中 |
| 依赖版本 | 使用最新 Svelte 5 / SvelteKit 2(修复已知 CVE) | 低 |
我的判断
Svelte 的安全性在默认路径上优于运行时框架,因为它把很多防御提前到编译阶段。但这种“默认安全”也容易让开发者产生错觉:以为只要用 Svelte 就不需要关注 XSS。
给开发者的建议:把 Svelte 当成“默认 textContent”的框架,而不是“自动消毒 HTML”的框架。{@html} 是你的让步点,像 React 的 dangerouslySetInnerHTML 一样对待它——非用不可时才用,且必须加消毒层。另外,关注 Svelte 编译时插槽(slot)的安全处理,未来可能会推出更严格的编译时检查。
最后,记住所有前端安全的核心原则:永远不要信任用户输入,框架只是缩小了犯错概率,消灭不了人类犯错。