标签模板构建防XSS引擎的核心是利用其语法特性与上下文感知转义,而非自动拦截;它分离模板与数据,支持按HTML文本、属性、事件等上下文精准转义,并强制区分可信/不可信内容。

用标签模板构建防 XSS 的脚本过滤引擎,核心不是靠它“自动拦截”,而是利用其语法特性 + 上下文感知转义逻辑,把危险输出扼杀在渲染前。它本身不提供安全,但能帮你写出更可控、更难出错的安全封装层。
理解标签模板的天然优势
标签模板(如html`)允许你拦截原始字符串和插值值,拿到结构化数据:字面量片段(strings)和待插入表达式(values)。这让你有机会在拼接前做判断——比如识别某个值即将出现在 HTML 文本、属性、事件处理器或 script 标签内,并施加对应转义。
- 它不执行任何默认转义,但给了你精确控制每个插值上下文的机会
- 相比普通字符串拼接,它天然分离“模板骨架”与“动态内容”,便于注入安全逻辑
- 配合
Symbol.toPrimitive或自定义类(如SafeHtml),可实现可信内容显式标记
实现上下文感知转义函数
关键在于:不能对所有插值统一调用 escapeHtml(),而要根据它将被插入的位置决定处理方式。
-
HTML 文本节点:转义
,>,&,",' -
双引号属性值:额外确保
"被编码为",避免属性截断 -
单引号属性值:确保
'被编码为' -
URL 属性(如 href):使用
encodeURIComponent()处理参数部分,而非全量 HTML 转义 - 内联事件(如 onclick):强烈建议禁止;若必须支持,需走 JS 字符串转义 + 严格白名单
示例函数骨架:
function html(strings, ...values) { let result = ''; for (let i = 0; i result += strings[i]; if (i const value = values[i]; // 这里需推断 value 即将出现的上下文 // 比如通过调用栈、模板字面量特征或显式注解 result += contextAwareEscape(value, inferredContext); } } return new SafeHtml(result); // 返回不可被误当字符串拼接的对象}
强制区分可信与不可信内容
标签模板无法替你判断内容是否可信,但它能帮你建立强约定:
- 所有普通变量插值(
${data.name})默认视为不可信,走严格转义 - 仅当显式包装为
raw(...)或trustedHtml(...)时才绕过转义 - 这类包装器应返回带
isTrusted: true标记的对象,且html函数内部检查该标记 - 一旦检测到未标记为可信却含
<script></script>、onerror=、javascript:等模式,直接抛错或静默替换
结合 CSP 与运行时防护补位
标签模板只管输出阶段,但 XSS 防御是纵深体系:
- 在响应头中设置
Content-Security-Policy: default-src 'self'; script-src 'self',禁用内联脚本和eval - 对最终生成的 HTML 字符串,在插入 DOM 前用
DOMPurify.sanitize()做最后一道过滤(尤其用于富文本场景) - 避免使用
innerHTML直接赋值;优先用textContent或createElement+appendChild - 若必须插入 HTML,确保只来自
SafeHtml实例,且该实例构造时已通过白名单校验










