← 返回题目列表

SSR 数据注水是什么?如何避免 XSS 和状态不一致?

高频 困难 第 15 / 25 题 更新于 2026/07/29
SSR数据注水XSSHydration

简化版

SSR 数据注水是把服务端渲染所需的初始数据序列化到 HTML 中,客户端 hydration 时复用这份数据,避免重复请求和首屏不一致。风险在于序列化不安全会造成 XSS,数据过大拖慢 HTML,数据和 HTML 不匹配会导致 hydration 问题。

详细版

常见形式:

<script id="__STATE__" type="application/json">
{"user":{"name":"Tom"}}
</script>

客户端读取并恢复状态。关键是不要把用户可控内容直接拼到可执行脚本里,要安全转义 <</script>、Unicode 分隔符等危险字符;敏感字段不要注水到前端;注水数据要和服务端 HTML 使用同一份快照。

完整版教学

一、为什么需要数据注水

SSR 服务端为了生成 HTML 已经取过一次数据。客户端 hydration 后如果再请求一次同样数据,既浪费网络,也可能因为数据变化造成界面不一致。

Server: fetch product -> render HTML
HTML:   商品价格 99
Client: 再 fetch product -> 商品价格 101
Hydration/首屏状态可能抖动

记忆钩子:数据注水就是把服务端用过的那桶水,顺手交给客户端继续用。

二、常见注水方式

<script>
  window.__INITIAL_STATE__ = {"product":{"id":1,"price":99}}
</script>

或者使用非执行 JSON 脚本:

<script id="__INITIAL_STATE__" type="application/json">
{"product":{"id":1,"price":99}}
</script>

第二种方式不会直接执行脚本内容,安全性更好一些,但仍要处理 HTML 解析边界。客户端通过 DOM 读取文本再 JSON.parse

三、XSS 风险来自字符串拼接

如果用户昵称是:

</script><script>alert(1)</script>

直接拼进脚本会提前闭合 script 标签并执行攻击代码。即使你使用 JSON.stringify,也要确保输出嵌入 HTML 时经过适合上下文的转义。

安全序列化至少要把 < 转成 \u003c,避免 </script> 被 HTML 解析器识别:

function safeSerialize(data) {
  return JSON.stringify(data).replace(/</g, '\\u003c')
}

真实项目应使用成熟序列化库或框架内置能力,不要手写半吊子转义。

四、不要注水敏感数据

注水数据会进入 HTML,用户能查看源码和 DevTools。服务端为了渲染可能拿到了手机号、权限、内部字段、token,但不代表这些都该发给浏览器。

数据是否适合注水
商品标题和价格适合
用户显示名视场景
access token不适合
内部成本价不适合
权限码全集谨慎

前端需要什么才传什么,敏感判断必须在服务端完成。不要为了省接口,把整个服务端模型直接序列化到页面。

五、数据大小和性能

注水会增加 HTML 体积。假设首屏 HTML 60KB,注水状态 300KB,首字节后还要下载更多 HTML,解析也更慢。过大的初始状态会抵消 SSR 首屏优势。

合理: 首屏必要数据 20KB
危险: 整个列表、筛选项、用户全量权限 500KB

优化方式包括只注水首屏必要字段、分页加载、压缩传输、把非关键数据延后客户端请求。SSR 不是把所有数据塞进 HTML。

六、保持 HTML 和状态同源快照

服务端渲染 HTML 和注水状态必须来自同一次数据快照。若 HTML 用数据 A,注水用数据 B,客户端恢复后会看到不一致。

render HTML with stateA
serialize stateB
client hydrate with stateB
-> 文本、列表长度、选中状态可能不一致

工程上应把数据获取结果放入统一上下文,渲染和序列化共用同一份对象。不要在渲染后又重新请求一次数据来生成状态。

七、常见误区与追问

  • 误区:JSON.stringify 后就绝对安全。 嵌入 HTML/script 上下文仍需处理 </script> 等解析边界。
  • 误区:服务端拿到的数据都可以注水。 注水内容对用户可见,敏感字段必须剔除。
  • 误区:注水越多越省请求。 过大 HTML 会拖慢下载和解析,损害首屏。
  • 追问:为什么注水能避免重复请求? 客户端 hydration 可复用服务端已取的数据。
  • 追问:如何减少 XSS 风险? 使用框架内置安全序列化或成熟库,按 HTML 上下文转义。
  • 追问:状态不一致怎么产生? HTML 和注水数据来自不同快照或客户端首次计算不同。

八、加强记忆

SSR 数据注水要记“复用、转义、瘦身、同源”:复用服务端首屏数据,序列化时按 HTML 上下文安全转义,只传首屏必要字段,HTML 和状态来自同一份快照。它能减少重复请求和水合抖动,但一旦处理不好,也会制造 XSS、泄密和性能问题。