模板字符串本身不防HTML注入,${}插值仅为纯字符串替换,无转义或校验;直接拼接HTML并用innerHTML插入将导致XSS,必须按输出上下文做对应转义或使用安全标签函数。

模板字符串本身不防 HTML 注入,它只是更方便的字符串拼接工具。${} 插值是纯字符串替换,不做任何转义或上下文校验。直接用它拼 HTML 并插入 DOM,就等于把用户输入原样放进页面——这正是 XSS 的温床。
关键不是“怎么用模板字符串防注入”,而是在使用模板字符串生成 HTML 时,必须配合上下文感知的安全处理机制。
? 模板字符串 + innerHTML = 危险组合
常见错误写法:
const userInput = '<script>alert("xss")</script>';
const html = `<div>${userInput}</div>`;
el.innerHTML = html; // ✅ 执行了脚本!
这里模板字符串没做任何防护,userInput 被原样嵌入,浏览器解析时执行脚本。
? 正确做法:按输出上下文做对应转义
| 输出位置 | 推荐方式 | 示例说明 |
|---|---|---|
插入 HTML 文本内容(如 ) |
使用 textContent 或手动 HTML 转义函数 |
el.textContent = userInput 安全;若必须拼 HTML,先转义 ,
|
插入 HTML 属性值(如 title="${val}") |
属性值需双重转义(HTML + 引号) | 用 DOMPurify.sanitize() 或专用 escape 函数处理后再插值 |
插入 <script></script> 内联代码或 JSON 数据 |
必须用 JSON.stringify() + template.JS(服务端)或 JSON.parse()(客户端) |
不要用 ${userInput} 直接拼 JS 字符串 |
例如安全拼接属性:
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/, '/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const name = '@@##@@';
const safeHtml = `<span title="${escapeHtml(name)}">Hover me</span>`;
? 更可靠的方案:用标签函数封装转义逻辑
定义一个自动转义的标签函数:
function html(strings, ...values) {
let result = '';
strings.forEach((str, i) => {
result += str;
if (i admin';
const output = html`<p>Welcome, ${user}!</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill12680" title="JavaScript 开发专家"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20dee630206.jpg" alt="JavaScript 开发专家" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill12680" title="JavaScript 开发专家" class="overflowclass">JavaScript 开发专家</a>
<p class="overflowclass">掌握包含 ES6+、异步模式和 Node.js API 的现代 JavaScript。处理 Promise、事件循环和浏览器/Node 兼容性。在 JavaSc</p>
</div>
<a rel="nofollow" href="/xiazai/skill12680" title="JavaScript 开发专家" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>`;
// → <p>Welcome, <b>admin</b>!</p>
这样既保留模板字符串的可读性,又把转义逻辑集中管控。
? 绝对禁止的“伪安全”操作
- ❌
const html =${userInput}+ el.innerHTML = html - ❌
const script =<script>${maliciousCode}</script>+ document.write() - ❌ 把用户数据塞进
${}后再用DOMPurify.sanitize()—— 顺序反了,应先净化再插值 - ❌ 用
template.HTML(Go)或dangerouslySetInnerHTML(React)绕过校验却不做前置过滤
? 补充提醒:服务端渲染场景
Node.js 中用模板字符串拼 HTML 时,同样不能依赖 ${} 自动防护。需搭配:
-
express+ejs/pug等模板引擎的自动转义(默认开启) - 或显式使用
he.escape()、marked()(带 sanitize)、DOMPurify等库预处理
比如:
res.send(`<h2>${he.escape(title)}</h2>`);
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










