必须对 &、、"、' 五个字符做html转义,否则用户输入的恶意代码将被浏览器直接执行;textcontent 安全显示纯文本,innerhtml 仅限严格过滤后的富文本渲染。

HTML 输出前不转义 & <code>> 就等于开后门
用户输入的 <script>alert(1)</script> 直接插入 HTML,浏览器会执行它。不是“可能被攻击”,而是“必然执行恶意代码”。关键在于:HTML 解析器只认字符本身,不看你“本意”是不是想显示这个符号。
必须对以下 5 个字符做转义(其他字符如 " ' 在属性值里才需处理):
-
&→& → <code>-
>→> -
"→"(仅在双引号属性中必要) -
'→'(仅在单引号属性中必要)
用 textContent 还是 innerHTML?看你是想“放内容”还是“放结构”
绝大多数场景下,你只是想把一段字符串原样显示出来——比如用户名、评论、错误提示。这时候 textContent 是最安全的选择,它根本不走 HTML 解析流程,所有字符都当纯文本处理。
只有当你明确需要渲染用户提交的富文本(且已做过严格过滤),才考虑 innerHTML。但注意:innerHTML 不会自动转义,写进去什么就解析什么。
- 显示用户昵称:
el.textContent = userInput✅ - 渲染后台返回的带格式公告:
el.innerHTML = sanitizeHtml(userInput)❗必须搭配可信的白名单过滤库(如DOMPurify),不能只靠正则 - 用
innerText替代?别,它受 CSS 影响(如display: none的内容不计入),行为不一致
服务端模板里 {{ }} 默认不转义?查清引擎默认行为
不同模板引擎对变量插值的默认策略完全不同。比如:
-
EJS:转义,不转义 -
Handlebars:{{data}}转义,{{{data}}}不转义 -
Vue模板:{{ data }}默认转义,v-html不转义 -
PHP Twig:{{ data }}默认转义,{{ data|raw }}不转义
一旦用了不转义语法,又没做额外过滤,XSS 就在那一行代码里埋好了。上线前务必确认模板层是否开启默认转义,别依赖“前端会处理”。
JSON 字符串塞进 HTML 属性?JSON.stringify() 不够,还得过一遍 HTML 转义
常见做法:把数据序列化成 JSON 放在 data-* 属性里,比如 <div data-payload='{"name":"<img src="https://img.php.cn/?x-oss-process=image/resize,p_40" alt="html标签如何防止XSS攻击_特殊字符转义必要性【技巧】" />"}'>。这里有两个陷阱:<ul>
<li>
<code>JSON.stringify() 只保证 JSON 合法,不处理 HTML 特殊字符 —— 单引号、、<code>& 照样能触发解析
JSON.stringify(),再对整个字符串做 HTML 实体转义(例如用 he.escape() 或自写映射),最后放进双引号属性:<div data-payload="" onerror="alert(1)" src="x>%22%7D%22%22"><p>这个组合动作容易漏掉第二步,尤其在快速迭代时——JSON 看似“安全”,实则只是换了个地方引爆。</p></div>











