xss中“闭合标签”是利用html解析器自动修复未闭合标签的容错机制实现注入,而非漏洞本身;浏览器自动补全dom结构,使恶意脚本得以执行,真实防御需按输出上下文做针对性编码而非简单过滤。

XSS攻击中“闭合标签”不是漏洞本身,而是利用HTML解析器对标签嵌套和结束符的容错机制实现注入——浏览器不会因标签未闭合而停止渲染,反而会自动补全或重排DOM结构,这给了攻击者插入恶意内容的空间。
HTML解析器如何处理未闭合标签
浏览器的HTML解析器(如WebKit的HTMLTokenizer、Blink的HTMLParser)在遇到未闭合标签时,会基于HTML5规范中的“栈式开放元素”规则进行自动修复。例如:
-
<div><script>alert(1) 不会报错,解析器会自动补上 <code></script></div>,但中间插入的脚本仍可能执行 -
<input value="<img src=x onerror=eval(atob('YWxlcnQoMSk='))>">中,双引号提前闭合后,onerror属性被解析为合法事件处理器 - 多个嵌套未闭合的
<span><div> <p> 会被解析器按上下文“合理收尾”,但收尾位置可能恰好把攻击载荷暴露在可执行上下文中 </p> <h3>为什么白名单过滤比黑名单更难绕过</h3> <p>黑名单依赖识别已知危险模式(如 <code><script></script>、onerror、javascript:),但HTML解析器允许大量变体:- 大小写混合:
<script></script>、<img src="x" onerror="..."> - 空格/制表符/换行干扰:
<img> - 编码混淆:
<script>在某些上下文中会被二次解码后执行 - 非标准属性名:部分老版本IE支持
onclick外的onmouseover、onfocus、甚至自定义事件
白名单策略(如只允许
<p></p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill3458" title="html-ppt-to-pdf"><img src="https://img.php.cn/upload/skill/000/000/081/178956546773641.jpg" alt="html-ppt-to-pdf" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill3458" title="html-ppt-to-pdf" class="overflowclass">html-ppt-to-pdf</a> <p class="overflowclass">将使用 `<section class="slide">` 约定的 HTML 幻灯片转换为高保真、矢量文本 PDF(使用 Playwright + Chromium 原生 PDF 功能)。</p> </div> <a rel="nofollow" href="/xiazai/skill3458" title="html-ppt-to-pdf" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>、<strong></strong>、href、src)直接切断所有未授权标签和属性的解析路径,不依赖“识别恶意”,而是“只放行已知安全”。DOM型XSS中闭合逻辑失效的真实原因
DOM型XSS不经过服务端,所以“闭合标签”本质是前端JS操作DOM时的上下文错位。典型场景:
-
document.getElementById("out").innerHTML = location.hash.slice(1);—— 输入#<img src="x" onerror="alert(1)">,innerHTML直接触发解析,此时没有服务端过滤,闭合与否完全由JS赋值位置决定 -
el.setAttribute("src", userInput)—— 若用户输入"javascript:alert(1)",属性值被当作JS URI执行,与标签是否闭合无关 -
eval("var name = '" + userInput + "'")—— 单引号闭合失败直接导致代码注入,这里根本不存在HTML标签结构
这类问题无法靠HTML过滤解决,必须严格区分数据来源和输出上下文,对
innerHTML、eval、setAttribute等高危API做语义级约束。真实拦截点不在标签层,而在上下文编码
现代防御真正起效的位置,是把用户输入按其**最终渲染上下文**做针对性编码,而非笼统“过滤标签”:
- 输出到HTML文本节点 → 使用
textContent或 HTML实体编码(→ <code><) - 输出到HTML属性值(
<div title="X">)→ 先用引号包裹,再对引号及特殊字符编码 <li>输出到JS字符串字面量(<code>var msg = "X";)→ 使用JSON序列化或JS字符串转义('→\') - 输出到URL参数(
href="/search?q=X")→ 使用encodeURIComponent()
绕过常发生在开发者误判上下文:比如把应进JS上下文的数据直接拼进HTML属性,或对已编码数据重复编码导致解码失效。最隐蔽的坑是模板引擎(如Vue/React)的
v-html或dangerouslySetInnerHTML—— 它们跳过框架默认防护,等价于裸调innerHTML。 - 大小写混合:










