导读:本期聚焦于高宇创作的《JavaScript 的模板字符串如何安全地嵌入用户输入以防止 XSS 攻击?》,敬请观看详情。把不可信数据直接拼进模板字符串再插入 DOM,是前端最典型的 XSS 入口之一。浏览器不会区分字符串里哪部分是结构、哪部分是数据,只要拼接结果被当成 HTML 解析就会执行恶意脚本。正确做法是在嵌入前对特殊字符做转义,或使用 textContent、innerText 等只写文本节点的接口。如果必须渲染富文本,应配合 DOMPurify 这类白名单清洗库,而不是手写正则。理解模板字符串只是语法糖、不改变字符串本质,才能从根源上避免注入。

在 Web 前端开发中,模板字符串因为支持多行和变量插值,常被用来组装 HTML 片段。但当插值内容来自用户输入框、URL 参数或第三方接口时,如果不加处理直接写入页面,就会形成反射型或存储型 XSS 漏洞。模板字符串本身只是 ECMAScript 2015 引入的语法糖,它让字符串拼接更直观,却并没有提供任何自动转义能力,最终生成的仍然是一个普通字符串,浏览器在解析这个字符串为 DOM 时不会区分代码与数据。

JavaScript 的模板字符串如何安全地嵌入用户输入以防止 XSS 攻击?

为什么模板字符串直接嵌入用户输入会触发 XSS

很多开发者误以为模板字符串比传统加号拼接更安全,其实两者在安全性上没有任何区别。假设页面上有一个评论框,用户输入 <img src=x onerror=alert(1)>,后端原样返回,前端用模板字符串组装列表:这时浏览器拿到的是一段包含标签结构的 HTML 文本,一旦通过 innerHTML 插入,onerror 中的脚本就会执行。模板字符串的 ${} 仅仅是把变量值转换为字符串并连接,完全不会分析内容是否含有尖括号或事件属性。

从解析原理看,HTML 文档的词法分析器在接收到 innerHTML 赋值时,会按标签、属性、文本节点规则重新构建 DOM 树。用户输入中的 <script><img onerror> 会被识别为元素而非文本,因此脚本逻辑被激活。以下代码展示了一个危险写法:

const userInput = '<img src=x onerror=alert(document.cookie)>';
const html = `<div class="comment">${userInput}</div>`;
document.getElementById('list').innerHTML = html;

上述代码中,userInput 来自不可信源,却未做处理。即便使用 textContent 之外的任何 HTML 写入接口,都会造成脚本执行。要注意,即便把输入放进 value 属性或 href 属性中,也可能通过 javascript: 协议触发脚本,因此不能只转义尖括号。

使用原生转义与文本接口阻断注入路径

最基础也最有效的防护方式,是区分“写文本”和“写结构”。如果目的只是展示用户提交的内容,应当使用 textContentinnerText,这两个接口会把赋值内容作为纯文本节点处理,浏览器不会对文本中的尖括号发起标签解析。改写前面的例子,只需创建元素并赋值文本:

const userInput = '<img src=x onerror=alert(1)>';
const div = document.createElement('div');
div.className = 'comment';
div.textContent = userInput;
document.getElementById('list').appendChild(div);

在这种方式下,页面上看到的是字面意义上的 <img src=x onerror=alert(1)>,而不会发起任何请求或弹窗。如果确实需要在模板字符串中生成 HTML,则必须手动转义关键字符:将 & 转成 &amp;< 转成 &lt;> 转成 &gt;、双引号转成 &quot;、单引号转成 &#39;。一个简单的转义函数如下:

function escapeHtml(str) {
  return str.replace(/&/g, '&')
            .replace(/</g, '<')
            .replace(/>/g, '>')
            .replace(/"/g, '"')
            .replace(/'/g, ''');
}
const safe = `<div>${escapeHtml(userInput)}</div>`;

这种转义方案能覆盖绝大多数场景,但缺点是需要开发者时刻记着调用,且对属性值中的 javascript: 伪协议无能为力。因此更推荐默认用 textContent,仅在可控结构中使用转义后的变量。

复杂富文本场景下的白名单过滤方案

当业务要求用户能提交带格式的富文本(例如论坛帖子、邮件模板),单纯转义会破坏合法标签,此时应使用基于白名单的 DOM 清洗库,例如 DOMPurify。它的原理是把字符串解析为 DOM 树,遍历每个节点和属性,移除不在白名单中的元素与事件处理器,再序列化回安全字符串。配合模板字符串时,先清洗再插入:

const dirty = '<p>hello</p><img src=x onerror=alert(1)>';
const clean = DOMPurify.sanitize(dirty);
const html = `<article>${clean}</article>`;
document.getElementById('content').innerHTML = html;

在上面的代码中,<p> 被保留,而 <img> 上的 onerror 被剥离,如果配置禁止 img 标签则整个标签消失。DOMPurify 支持自定义标签和属性白名单,也能处理 SVG、MathML 等复杂命名空间,比手写正则稳健得多。要注意,即便使用此类库,也应避免把清洗后的内容通过 evalsetTimeout 字符串形式执行,因为那已经脱离 HTML 解析范畴。

除了前端清洗,后端在存储和返回数据时也应做一道校验,例如限制字段长度、过滤控制字符,并在 HTTP 响应头加上 Content-Security-Policy 来禁止内联脚本。前后端协同才能把 XSS 风险降到最低。模板字符串作为书写便利工具,只要牢记“插值即数据、写入需转义、富文本靠白名单”的原则,就能在保持代码清晰的同时保障应用安全。

模板字符串用户输入XSS防护修改时间:2026-08-22 09:34:58

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。