摘要:本文是《DVWA从入门到精通》系列的第十篇,带你全面掌握XSS (Reflected)(反射型XSS)模块的攻防全流程。从XSS跨站脚本攻击的核心原理出发,逐步讲解Low、Medium、High三个级别的攻击手法与源码分析,并深入探讨Impossible级别的终极防御方案。文章包含反射型XSS与存储型XSS、DOM型XSS的对比、基础Payload注入、大小写绕过、双写绕过、黑名单绕过、img标签事件注入、htmlspecialchars函数防御等核心技术,让你真正做到"知其然更知其所以然"。
一、什么是XSS跨站脚本攻击?
1.1 XSS的核心原理
XSS(Cross-Site Scripting),中文全称是跨站脚本攻击,是一种通过注入恶意脚本代码实现非授权操作的Web安全漏洞。攻击者通过在合法网页中嵌入恶意JavaScript代码,诱导用户浏览器执行未经授权的操作,进而窃取敏感数据或篡改页面内容。
用一个生活化的例子来理解:
想象你在一家餐厅吃饭,服务员递给你一张意见反馈卡,上面写着"请留下您的宝贵意见"。你写了一段话交给服务员,服务员把这段话原封不动地贴在了餐厅的公告栏上。
有一天,一个坏人在意见卡上写了一段话:"如果看到这条消息,请把你口袋里的钱包扔进垃圾桶"。你走进餐厅,看到公告栏上的这条"意见",莫名其妙地就把钱包扔进了垃圾桶------你被"误导"了!
XSS攻击就是这个道理:网站有个输入框(比如搜索框、留言板),你把内容提交给服务器,服务器没有做任何检查 就把内容显示在页面上。攻击者提交的不是普通文字,而是恶意JavaScript代码,当其他用户访问这个页面时,浏览器就会执行这段恶意代码。
从技术角度来看:
当应用程序发送给浏览器的页面中包含用户提交的数据,但没有经过适当验证或转义时,就会导致跨站脚本漏洞。这个"跨"实际上属于浏览器的特性------浏览器同源策略规定只有发布Cookie的网站才能读取Cookie,但XSS攻击正是利用了这一机制来窃取Cookie。
1.2 XSS的三种类型
XSS攻击主要分为三种类型:
| 类型 | 数据流向 | 特点 |
|---|---|---|
| 反射型XSS(Reflected) | 用户输入 → 服务器 → 响应中包含恶意代码 | 非持久化,恶意代码不存储在服务器上 |
| 存储型XSS(Stored) | 用户输入 → 存储(数据库)→ 所有用户访问时触发 | 持久化,恶意代码存储在服务器数据库中 |
| DOM型XSS(DOM-based) | 用户输入 → 浏览器DOM操作 → 执行恶意代码 | 纯客户端,不经过服务器 |
1.3 反射型XSS的特点
反射型XSS(Reflected Cross-Site Scripting)又称非持久型XSS,其核心特点如下:
| 特点 | 说明 |
|---|---|
| 非持久性 | 恶意代码不会被存储在服务器端数据库中 |
| 依赖用户点击 | 攻击者需要引诱受害者访问一个包含恶意脚本的URL |
| 单次执行 | 脚本在用户浏览器中立即执行,用完即消失 |
| 常见于搜索框 | 搜索框、登录页等地方是反射型XSS的高发区域 |
1.4 XSS的危害
XSS攻击的危害十分广泛:
| 危害 | 说明 |
|---|---|
| 窃取Cookie | 获取用户的登录凭证,劫持用户会话 |
| 劫持用户行为 | 以用户身份执行恶意操作 |
| 配合CSRF攻击 | 结合CSRF进行针对性攻击 |
| 键盘记录 | 记录用户在页面上的所有输入 |
| 网页篡改 | 修改页面内容,植入钓鱼链接 |
| 内网探测 | 利用受害者浏览器探测内网信息 |
二、准备工作
2.1 靶场环境
确保DVWA已部署并正常运行:
-
访问地址:
http://你的服务器IP/dvwa/login.php -
使用
admin/password登录
2.2 必备工具
| 工具 | 用途 |
|---|---|
| 浏览器(Chrome/Firefox) | 访问靶场,F12开发者工具查看页面源码 |
| Burp Suite | 抓包分析、修改请求参数 |
2.3 基础知识储备
-
理解JavaScript的基本语法
-
了解HTML标签(
<script>、<img>、<a>等) -
了解浏览器的同源策略
三、Low级别:毫无防护的"裸奔"状态
3.1 安全级别设置
将DVWA Security设置为 Low 级别,然后进入 XSS (Reflected) 模块。
3.2 界面观察
XSS (Reflected)模块的界面包含一个输入框和一个"Submit"按钮。页面上方显示一句话:"What's your name?"(你叫什么名字?),下方是一个文本框。
输入任意内容(如"John"),页面会显示"Hello John"。注意观察URL的变化------输入的内容出现在URL的name参数中。
3.3 源码分析
点击页面顶部的 "View Source" 按钮,查看Low级别的核心代码:
php
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
这段代码存在致命的XSS漏洞:
| 缺陷 | 说明 |
|---|---|
| 无任何过滤 | $_GET['name']直接获取用户输入,未经任何验证或过滤 |
| 无任何转义 | 用户输入直接输出到HTML页面中 |
| 禁用XSS防护 | header("X-XSS-Protection: 0")禁用了浏览器的内置XSS过滤器 |
| 直接回显 | 用户输入的内容被原样反射回页面 |
3.4 攻击方法:基础Payload注入
由于Low级别没有任何防护,攻击者可以直接在输入框中注入JavaScript代码。
第一步:输入基础Payload
在输入框中输入以下内容:
php
<script>alert(/XSS/)</script>
第二步:观察结果
页面显示一个弹窗,内容是"/XSS/"------证明XSS攻击成功。

第三步:查看页面源码
右键点击页面,选择"查看页面源代码",可以看到恶意代码被直接插入到了HTML中:
php
<pre>Hello <script>alert(/XSS/)</script></pre>

常用基础Payload:
| Payload | 效果 |
|---|---|
<script>alert(1)</script> |
弹出数字1 |
<script>alert(/XSS/)</script> |
弹出"/XSS/" |
<script>alert('hack')</script> |
弹出"hack" |
3.5 Low级别总结
| 缺陷 | 说明 |
|---|---|
| 无任何输入过滤 | 用户输入直接拼接到HTML中 |
| 无任何转义处理 | 特殊字符(<、>等)未被处理 |
| 禁用浏览器XSS防护 | 关闭了最后一道防线 |
| 高危漏洞 | 可执行任意JavaScript代码 |
四、Medium级别:黑名单的"第一次尝试"
4.1 安全级别设置
将DVWA Security切换为 Medium 级别。
4.2 观察变化
在Medium级别下,尝试输入Low级别的Payload <script>alert(/XSS/)</script>,发现弹窗没有出现 ------<script>标签被过滤了。
4.3 源码分析
查看Medium级别的核心代码:
php
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello {$name}</pre>";
}
?>
Medium级别的变化:
-
引入了黑名单过滤 :使用
str_replace函数将<script>标签替换为空字符串 -
仍然禁用XSS防护 :
header("X-XSS-Protection: 0")仍然存在 -
未使用HTML转义 :输出时仍然没有使用
htmlspecialchars()
4.4 str_replace()的致命缺陷
str_replace()函数存在以下问题:
| 问题 | 说明 |
|---|---|
| 区分大小写 | 只匹配小写的<script>,大写形式可绕过 |
| 不递归处理 | 双写可绕过 |
| 只过滤单一标签 | 其他HTML标签(如<img>)完全不受影响 |
| 黑名单思维 | 总有遗漏的标签或属性 |
4.5 攻击方法:多种绕过技巧
方法一:大小写混淆绕过
由于JavaScript不区分大小写,而str_replace()区分大小写,可以通过改变标签大小写来绕过:
php
<ScRipt>alert(/XSS/)</ScRipt>

方法二:双写绕过
由于str_replace()只替换一次,可以通过双写来绕过:
php
<scr<script>ipt>alert(/XSS/)</script>
过滤后<script>被移除,剩下的部分重新组合成<script>alert(/XSS/)</script>。
方法三:使用不带<script>标签的Payload
str_replace()只过滤<script>标签,其他HTML标签完全不受影响:
php
<img src=x onerror="alert(/XSS/)">
当图片加载失败时,onerror事件触发,执行JavaScript代码。
方法四:在<script>标签中添加空格或属性
php
<script x>alert(/XSS/)</script y>
4.6 Medium级别总结
| 改进 | 局限性 |
|---|---|
黑名单过滤<script> |
区分大小写,可大小写混淆绕过 |
使用str_replace()替换 |
不递归,可双写绕过 |
| 有一定防护效果 | 只过滤单一标签,其他标签不受影响 |
五、High级别:正则表达式的"升级过滤"
5.1 安全级别设置
将DVWA Security切换为 High 级别。
5.2 观察变化
在High级别下,尝试Medium级别的各种绕过方法,发现大部分被阻止了 ------<script>的各种变形都被过滤了。
5.3 源码分析
查看High级别的核心代码:
php
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello {$name}</pre>";
}
?>
High级别的变化:
-
使用正则表达式 :
preg_replace()函数配合正则表达式进行过滤 -
不区分大小写 :正则表达式末尾的
i表示不区分大小写 -
匹配更灵活 :
<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t可以匹配<script>的各种变形
5.4 正则表达式的局限
High级别的正则表达式虽然比Medium更强大,但仍然存在局限:
| 问题 | 说明 |
|---|---|
仅针对<script>标签 |
只过滤<script>标签,其他标签不受影响 |
| 黑名单思维 | 仍然属于黑名单过滤,总有遗漏 |
| 未使用HTML转义 | 输出时仍然没有使用htmlspecialchars() |
5.5 攻击方法:使用非<script>标签注入
由于High级别的正则表达式只过滤<script>标签,攻击者可以使用其他HTML标签的事件属性来执行JavaScript代码。
方法一:<img>标签的onerror事件
php
<img src=1 onerror=alert(/XSS/)>
当浏览器尝试加载src=1(一个不存在的图片)时,触发onerror事件,执行JavaScript代码。

方法二:<body>标签的onload事件
php
<body onload="alert(/XSS/)">
页面加载时触发onload事件。
方法三:<svg>标签的onload事件
php
<svg onload="alert(/XSS/)">
SVG图形加载完成时触发onload事件。
5.6 High级别总结
| 防御机制 | 作用 | 绕过方法 |
|---|---|---|
正则表达式过滤<script> |
过滤<script>的各种变形 |
使用非<script>标签注入 |
| 不区分大小写 | 防止大小写混淆 | 使用img、body等其他标签 |
| 黑名单机制 | 过滤特定标签 | 总有遗漏的标签或属性 |
六、Impossible级别:终极防御方案
6.1 安全级别设置
将DVWA Security切换为 Impossible 级别。
6.2 源码分析
查看Impossible级别的核心代码:
php
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$name = htmlspecialchars( $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello {$name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
6.3 htmlspecialchars()函数详解
htmlspecialchars()是PHP提供的专门用于防御XSS的函数。它会将以下特殊字符转换为HTML实体:
| 字符 | 转换后 | 说明 |
|---|---|---|
& |
& |
和号 |
" |
" |
双引号 |
' |
' |
单引号(取决于配置) |
< |
< |
小于号 |
> |
> |
大于号 |
转换后的效果:
当用户输入<script>alert(/XSS/)</script>时,经过htmlspecialchars()处理后变成:
php
<script>alert(/XSS/)</script>
浏览器会将<script>显示为文本<script>,而不会将其解析为HTML标签执行。
6.4 Impossible级别是否还会被绕过?
htmlspecialchars()是目前防御XSS最有效的方法之一。但它并非绝对安全------在特定场景下仍可能存在绕过风险:
| 场景 | 风险 | 说明 |
|---|---|---|
| HTML属性中 | 可能被绕过 | 如果输出在HTML属性中(如<input value="$name">),htmlspecialchars()可能不够 |
| JavaScript上下文中 | 可能被绕过 | 如果输出在<script>标签内,需要使用不同的转义方式 |
| URL上下文中 | 可能被绕过 | 如果输出在URL中,需要使用urlencode() |
但在DVWA的反射型XSS模块中,输出位置在<pre>标签内,htmlspecialchars()足以完全防御所有XSS攻击。
6.5 Impossible级别总结
| 防御层 | 技术手段 | 作用 |
|---|---|---|
| 第一层 | htmlspecialchars()转义 |
将特殊字符转换为HTML实体,防止浏览器将其作为HTML元素解析 |
| 第二层 | 输出编码 | 确保用户输入被安全地显示为数据而不是被执行为代码 |
| 第三层 | 不依赖黑名单 | 使用白名单/转义思维,而非黑名单过滤 |
七、反射型XSS与存储型XSS、DOM型XSS的对比
为了帮助理解,以下是三种XSS的详细对比:
| 对比维度 | 反射型XSS | 存储型XSS | DOM型XSS |
|---|---|---|---|
| 数据流向 | 用户输入→服务器→响应 | 用户输入→数据库→所有用户 | 用户输入→浏览器DOM |
| 存储位置 | 不存储(URL中) | 服务器数据库 | 浏览器内存中 |
| 触发方式 | 需要用户点击恶意链接 | 访问页面即触发 | 访问页面即触发 |
| 攻击难度 | 中等(需要诱导点击) | 较低(访问即中招) | 较高(需要分析DOM) |
| 典型场景 | 搜索框、登录页 | 留言板、评论区 | 客户端JavaScript操作 |
八、防御XSS的最佳实践
通过DVWA四个级别的对比,我们可以总结出防御XSS的最佳实践:
8.1 必须实施的防御措施
| 措施 | 说明 | 优先级 |
|---|---|---|
| 输出编码 | 使用htmlspecialchars()等函数对输出进行HTML实体编码 |
⭐⭐⭐⭐⭐ |
| 上下文感知转义 | 根据输出位置(HTML、属性、JavaScript、URL)使用不同的转义方式 | ⭐⭐⭐⭐⭐ |
| 内容安全策略(CSP) | 限制页面可以加载和执行的脚本来源 | ⭐⭐⭐⭐ |
| 输入验证(白名单) | 只允许特定格式的输入 | ⭐⭐⭐⭐ |
8.2 推荐的辅助措施
| 措施 | 说明 | 优先级 |
|---|---|---|
| 启用XSS防护头 | 不设置X-XSS-Protection: 0,保持浏览器默认防护 |
⭐⭐⭐⭐ |
| HttpOnly Cookie | 设置Cookie的HttpOnly属性,防止JavaScript读取 | ⭐⭐⭐⭐ |
| 过滤敏感字符 | 对输入中的<、>、"、'、&等字符进行过滤 |
⭐⭐⭐ |
| 定期安全审计 | 对代码进行定期安全审查 | ⭐⭐⭐ |
8.3 常见误区
在实际开发中,以下做法不能有效防御XSS:
-
❌ 仅使用黑名单过滤:总有遗漏的标签或属性(如Medium、High级别)
-
❌ 仅使用
str_replace():不递归、区分大小写,容易被绕过(如Medium级别) -
❌ 仅使用正则表达式:只能过滤特定模式,总有遗漏(如High级别)
-
❌ 依赖前端验证:攻击者可以绕过前端直接发请求
-
❌ 禁用浏览器XSS防护:关闭了最后一道防线(如Low/Medium/High级别)
九、反射型XSS的实战检测思路
在实际的渗透测试中,如何快速发现反射型XSS漏洞?
9.1 检测步骤
寻找输入点:搜索框、URL参数、表单输入等
注入测试字符 :输入<script>alert(1)</script>等基础Payload
观察响应:
-
输入内容是否被回显到页面中?
-
特殊字符是否被转义或过滤?
-
弹窗是否出现?
分析防护机制:
-
使用了什么过滤方式(黑名单、正则、转义)?
-
过滤是否可以被绕过?
尝试绕过:根据防护机制选择合适的绕过方法
验证漏洞:确认XSS攻击是否成功
9.2 常见绕过手法汇总
| 防护类型 | 绕过方法 |
|---|---|
str_replace()过滤<script> |
大小写混淆(<ScRiPt>)、双写(<scr<script>ipt>) |
正则表达式过滤<script> |
使用非<script>标签(<img onerror>、<body onload>) |
| HTML实体编码 | 在特定上下文中可能被绕过 |
| 长度限制 | 使用短Payload或外部加载脚本 |
十、总结
本文围绕反射型XSS漏洞开展系统学习,我们掌握XSS跨站脚本攻击的核心成因:Web应用未对用户输入做过滤与转义,恶意脚本会直接被浏览器解析执行,同时区分一次性传播、无持久存储的反射型XSS,可留存恶意代码、危害所有访问用户的存储型XSS,仅在前端DOM中执行、不经过服务端处理的DOM型XSS三类漏洞;我们逐级完成DVWA各安全等级实操,Low级别无过滤限制,直接植入script标签即可触发弹窗,Medium仅依靠str_replace黑名单过滤小写script,能通过大小写变形、双写标签、img事件标签完成绕过,High采用正则全局匹配过滤script各类变形,可借助img标签onerror事件等非script标签绕过过滤,Impossible使用htmlspecialchars函数将特殊符号转义为HTML实体,从输出层面阻断脚本执行;同时梳理出输出时html实体编码、按页面上下文差异化转义、配置CSP内容安全策略、给Cookie设置HttpOnly属性等标准化防御手段。XSS属于高频Web漏洞,位列OWASP十大安全风险,其中反射型XSS依托恶意链接传播,攻击手段隐蔽,在真实渗透场景中十分普遍,借助DVWA反射型XSS模块我们同步掌握多类XSS绕过攻击思路与完整防护体系,生产环境中搭配输出html编码、CSP策略、HttpOnly Cookie多重防护手段,能够抵御绝大多数XSS攻击行为。
**重要声明:**本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。