动态ssr渲染放大安全风险:属性注入因引号闭合+事件绑定更危险,富文本须服务端白名单净化,ssr输出必须为严格闭合的纯fragment以避免hydration静默失败。

动态 SSR 渲染本身不增加安全性,反而放大风险——服务端拼出的 HTML 一旦含未转义用户数据,就直接绕过前端所有防护,且 hydration 失败常被误判为“样式问题”而忽略。
属性值注入:为什么 data-id="{{ userInput }}" 比 <div>{{ userInput }}</div> 更危险
属性上下文允许引号闭合 + 事件绑定,浏览器解析时会提前截断并执行后续 JS。例如 userInput = '" onmouseover="alert(1)" x="' 插入到 <div data-id="{{ userInput }}"> 中,最终生成 <code><div data-id="" onmouseover="alert(1)" x="">,<code>onmouseover 立即触发。
- EJS/Nunjucks 等模板中,
不转义,是显式关闭转义,务必避免 - Vue SSR 中
:data-id="userInput"安全(自动调用escapeAttribute),但data-id="{{ userInput }}"危险(纯字符串插值) - Node.js 的
he.escape()默认只处理、<code>>、&,对引号无效;必须用he.escapeAttribute()或手动input.replace(/"/g, """).replace(/'/g, "'")
富文本渲染:为什么服务端净化不能省,客户端再跑一遍 DOMPurify 反而有害
服务端吐出已清洗的 HTML,客户端又用 DOMPurify 二次处理,不仅浪费 CPU,还可能因规则不一致导致标签被误删(如服务端允许 <figure></figure>,客户端配置漏掉),引发 hydration 差异或样式错乱。
- 服务端必须用白名单净化:Node.js 推荐
sanitize-html,配置allowedTags: ['p', 'br', 'strong', 'a']、allowedAttributes: { a: ['href'] }、allowedSchemes: ['http', 'https', '/'] - 客户端渲染前仍需
DOMPurify.sanitize(),但配置必须和服务端完全一致,否则 hydration 失败 - 禁用黑名单模式:
FORBID_TAGS: ['script']无效,DOMPurify默认已禁用高危标签,重点是显式声明ALLOWED_TAGS











