DVWA实战:反射型XSS从入门到防御全解析
2026/9/18 4:37:13 网站建设 项目流程

1. 从DVWA认识反射型XSS:先搞清楚你在打什么

第一次打开DVWA靶场,很多人点进XSS(Reflected)模块后是一脸懵的——页面上就一个输入框和一个Submit按钮,输入什么就回显什么,看不出任何门道来。这其实是学习XSS最好的起点,因为反射型XSS(Reflected Cross-Site Scripting)是最基础、最直观、也最容易理解的一类Web漏洞,你在DVWA里做的每一步操作,都能在真实业务系统中找到一一对应的场景。

DVWA(Damn Vulnerable Web Application)是一个开源的PHP+MySQL环境靶场,专门用来练习Web安全测试。它不是真实的“漏洞网站”,而是一个合法、可控、供安全从业者和爱好者练手的实验平台。这一点很重要——你在这里做的所有攻击测试,都是被授权的、安全环境内的行为,和去真实网站“打点”完全是两回事。靶场里每个漏洞模块都分了Low、Medium、High、Impossible四个难度级别,这四个级别实际上就是真实互联网上Web应用安全防护水平从裸奔到及格线的缩影。

反射型XSS的核心特征是“一次性”和“即时反射”。攻击者把恶意脚本写在URL参数里,用户点开这个构造好的链接,页面把参数值原样输出到HTML里,脚本就在用户的浏览器里执行了。注意,脚本是在“用户自己的浏览器”里执行的,不是在服务器上执行——这一点很多人刚接触时会搞混。XSS攻击的是用户,而不是服务器,所以它的危害往往是以用户的身份发起请求、窃取用户的数据、盗取用户的Cookie等等。

先入个门,把XSS家族的关系理清楚。反射型XSS(Reflected)是“你发给我一个链接,我点开就中招”;存储型XSS(Stored)是“你发一段评论,存在了服务器上,以后每个看评论的人都中招”;DOM型XSS(DOM Based)则是“页面本身的JavaScript代码在操作DOM时把不可信数据拼进了动态渲染里”。回头你打DVWA的时候,会连着三个XSS模块都过一遍,它们之间的差别会在实战中越刻越清楚。

这篇文章我们就只聚焦在反射型XSS这一个点上,把DVWA的Low、Medium、High三个难度全部走一遍,不只要告诉你“怎么过关”,更重要的是讲清楚“为什么这样写能绕过”“被过滤掉了该怎么想下一种方案”以及“真正开发时该怎么防住这类问题”。毕竟打靶场的目的不是截图打卡,而是把攻击手法和防御思想内化成自己的判断力。

2. 环境准备:15分钟把DVWA跑起来

工欲善其事,必先利其器。DVWA的搭建方式有很多,这里我推荐“PHP内置服务器 + 手工配置”的方式,原因是我觉得用Docker虽然一条命令就能搞定,但少了手工配置的过程,你对Web服务、数据库连接、配置文件这些底层东西的理解会薄弱不少。学安全的初期,我还是建议动手多一点,出错多一点,记忆才牢靠一点。

2.1 三种搭建方式怎么选

DVWA的搭建方案我梳理下来,主要有下面三条路:

  • Docker一键部署:代码仓库里有docker-compose配置,一条命令拉起环境。优点是快,缺点是环境太“黑盒”,你连不上数据库时很难判断是哪一层出了问题。
  • PHPStudy/宝塔面板 + 手动部署:适合在Windows上操作的初学者,图形化管理MySQL和Apache/Nginx,排查问题方便。
  • 原生LAMP/LNMP手工部署:最折腾,但也最练习基本功。我建议有点Linux基础的同学走这条路。

我个人在给身边朋友推荐的时候,默认是Windows下用PHPStudy,因为国内学安全的同学很多主力机就是Windows,PHPStudy装好了Apache和MySQL,DVWA丢进去就能跑,省去很多不必要的环境折腾。

2.2 手工配置DVWA的关键节点

如果你选择手工配置,需要注意几个关键点。下载DVWA源码后,解压放到Web根目录,进入config目录,把config.inc.php.dist复制一份重命名为config.inc.php,然后打开编辑。需要修改的核心配置在数据库连接部分:

$_DVWA[ 'db_server' ] = '127.0.0.1'; $_DVWA[ 'db_database' ] = 'dvwa'; $_DVWA[ 'db_user' ] = 'dvwa'; $_DVWA[ 'db_password' ] = 'p@ssw0rd';

默认的用户名密码不是root/123456,而是dvwa/p@ssw0rd。很多人卡在这一步,数据库连不上就一直白屏,其实是这里没改。改成你本机实际的MySQL账号密码即可。

浏览器访问http://127.0.0.1/dvwa,会进入安装页面,点一下“Create / Reset Database”按钮,出现Database has been created说明数据库初始化成功。然后使用默认账号admin/password登录。初次登录后DVWA会提示你修改密码,改一个自己记得住的就行。

注意:DVWA的PHP版本兼容性是个大坑。如果你用的是PHP 8.1以上版本,旧版DVWA很可能出现一些函数弃用警告,甚至直接白屏。建议对应DVWA官方仓库的Release版本选择匹配的PHP环境,1.9版配PHP 5.6,最新版(2.x系列)对PHP 7.4/8.0都兼容。实测下来,PHP 7.4是最稳的,省心。

登录进去之后,先把页面右下角或者菜单里的Security Level(安全级别)设置为Low。DVWA的XSS模块在最左侧菜单的XSS(Reflected),点进去就是我们要打的目标了。

3. Low级别:连过滤都没有,这是最原始的XSS

3.1 界面分析和通关操作

Low级别的反射型XSS页面特别简单,核心代码逻辑也很直白。你输入一个名字,点Submit,它把名字放到一个Hello开头的话里回显出来。我第一次打这个级别的时候,第一反应是在输入框里敲几个字母测一下回显位置,比如输入zhangsan,页面显示:

Hello zhangsan.

然后我直接输入了XSS最经典的验证Payload:

<script>alert('XSS')</script>

页面弹出XSS提示框,弹窗的那一刻,说明脚本在用户的浏览器上下文里执行成功了。过关条件达成。

3.2 Low级别的源码解读

DVWA的Low级别源码并不复杂,核心逻辑就是把$_GET['name']获取到的参数值,原封不动地拼接进HTML里输出。也就是说,用户的输入从进入系统到被浏览器解析,中间没有任何过滤、转义、白名单校验。这里要记住一个关键结论:凡是把用户可控数据直接拼接进HTML输出的位置,都存在XSS的潜在可能,是否有过滤只决定利用难度,不决定漏洞是否存在。

用浏览器的开发者工具看网络请求会更直观。点击Submit后,Network面板里能看到URL变成了:

http://127.0.0.1/dvwa/vulnerabilities/xss_r/?name=%3Cscript%3Ealert%28%27XSS%27%29%3C%2Fscript%3E

%3C就是<的URL编码形式。这说明数据是通过GET请求的name参数传进去的。在真实攻击场景里,攻击者会把这个完整URL发给受害者,受害者已经登录了某个系统,点开链接就触发脚本。

实操心得:在实验的时候建议打开浏览器开发者工具的“Preserve log”,这样每次提交后看到的还是上一次的完整请求和响应内容。很多时候问题不在Payload本身,而是你看不到浏览器究竟把什么发送给了服务器。

3.3 Low级别的核心技术点总结

Low级别虽然简单,但有几个点值得消化:

  • GET参数反射回显:输入点、输出点都在同一个请求里,这是反射型XSS的基本结构。
  • 无过滤无转义:服务器直接拼接输出,这是漏洞存在的根本原因。
  • 浏览器解析执行<script>标签被浏览器当成HTML结构解析,脚本随即执行。
  • 风险等级:低级难度对应真实世界中“完全没做安全防护”的老旧应用,教你认识最原始的漏洞形态。

4. Medium级别:过滤了<script>,但你还有一百种写法

进入Medium级别后,先试试老套路。输入<script>alert('XSS')</script>,点击提交,页面没有弹窗,而是把这段内容原样显示出来了,还夹了一串乱码。这时候网上很多教程会让你直接换成<img src=x onerror=alert('XSS')>,一试就弹窗了。但如果你只停留在“抄Payload过关”的层面,遇到下一个变种就又会卡住。我们得搞清楚Medium到底过滤了什么。

4.1 Medium级别的过滤逻辑

DVWA的Medium级别源码里,对输入做了这样一个处理:

$name = str_replace( '<script>', '', $_GET[ 'name' ] );

也就是说它对<script>这个字符串做了替换,替换成空字符串。这句话透露的信息特别关键:它过滤的只是小写字母的<script>这一个固定字符串,而不是“脚本标签”这个HTML概念。明白了这一点,绕过的思路就有了。

第一,大小写绕过。<script>被替换了,那<SCRIPT><ScRiPt>呢?这些变体都能绕过基于固定字符串匹配的过滤,浏览器解析HTML标签时不区分大小写,所以<SCRIPT>alert('xss')</SCRIPT>照样能弹窗。

第二,双写绕过。因为过滤是“把<script>删掉”,那你输入<scr<script>ipt>,它把中间的<script>删掉后就成了<script>,完美接合。这种绕过方式在真实漏洞中也很常见,很多WAF规则做简单字符串替换时都踩过这个坑。

第三,换标签。<script>被过滤了,关键是“能执行JavaScript事件的标签”不止它一个,比如<img src=x onerror=...><svg onload=...><body onload=...>都很经典。它们不需要一个完整的<script>标签,而是通过事件属性触发脚本执行。

4.2 Medium级别的完整通关路径

实际到我操作的时候,Medium级别我试的顺序是这样的。为了验证过滤规则,我首先输入一段普通文本,确认回显正常。然后输入<script>alert(1)</script>,排除干扰,再输入大写版本<SCRIPT>,弹窗成功,这说明过滤不是大小写不敏感的。

如果测试环境对大小写也做了处理,我就会换用事件标签来打。这里我推荐用<img src=x onerror=alert(document.cookie)>来验证。只要图片加载失败(src的值x不存在),就会触发onerror事件来执行JavaScript。在DVWA里弹一下Cookie,你会立刻意识到如果这是一台真实业务的服务器,攻击者可以用这种方式偷走用户的会话凭证。

注意:Medium级别虽然过滤了<script>,但页面还是会处理其他HTML标签的,所以用<img>这类标签方式完全合法,弹窗后即视为通过。测试时建议把浏览器的缓存关掉,避免每次都用旧页面影响判断。

4.3 Medium级别暴露的常见认知误区

我见过很多初学者在Medium级别犯同一个错误:试了一两种Payload没生效,就急着去网上搜答案,搜到<img>绕过后就完事了。这样做虽然能过关,但学习效率很低。真正应该做的是在过关之后回头推演一遍:服务器究竟过滤了什么?为什么这个Payload能生效?如果过滤规则改成“过滤所有尖括号”,我还能用什么办法?

这一个自我提问的训练,比刷十个靶场都值钱。我在带人的时候常说一句话:不要背Payload,要背绕过思路。Payload是死的,WAF规则是活的,只有思路能跟着场景变。

5. High级别:正则过滤下怎么找活路

High级别的页面和Low、Medium从外表上看没什么区别,但同一句话回显出来,输入<script>alert('XSS')</script>已经不再弹窗。查看源码你会发现,DVWA在High级别使用了正则表达式来过滤:

$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );

这串正则是什么意思呢?它匹配的是“以<开头,中间任意内容,然后按顺序出现script这几个字母”,并且是大小写不敏感的(末尾的i修饰符)。这意味着:你可以在s c r i p t这些字母之间插入任意字符,只要连起来能构成script,照样会被删掉。所以<scr<script>ipt>双写在这里还有用吗?我们推演一下:正则匹配到<script>之后替换成空字符串,但因为没有循环处理,它删掉一个匹配后就把结果输出了。双写<scr<script>ipt>被处理后变成<script>,还是能逃过一劫——不过这只是针对这一条特定规则而言。

5.1 High级别的过滤短板在哪里

这条正则看着挺唬人,其实短板也很明显。它只处理了<script>这一个标签,完全没有考虑其他标签和JavaScript事件。所以<img src=1 onerror=alert('xss')>这类Payload在这个规则下畅通无阻,因为里面压根就不含script这个词。

另一个漏洞是它的.*配的是“非换行任意字符”,而且在Perl兼容正则里,.*默认是贪婪匹配,并且可以匹配尖括号本身。你构造特殊Payload时可以利用这个特性做更多变形。不过作为练习,High级别的目标就是让你意识到:只过滤danger标签的黑名单思路是有穷的,总有你想不到的标签可以触发脚本。

客观说一句,DVWA的High级别在实际对抗强度上不算高,它更像是在提醒你:黑名单过滤不靠谱。真正安全的做法是白名单校验——只用允许的字符,其余一律拒绝。这才是High级别更值得深思的地方。

5.2 High级别的实操验证过程

我实际在High级别下做了一组测试,记录一下测试结果:

输入Payload页面输出是否会弹窗
<script>alert(1)</script>内容被清除
<SCRIPT>alert(1)</SCRIPT>内容被清除
<scr<script>ipt>alert(1)</scr</script>ipt>输出为<script>alert(1)</script>
<img src=x onerror=alert(1)>原样输出
<svg onload=alert(1)>原样输出
"><svg onload=alert(1)>原样输出是(取决于上下文)

从表格可以明显看出,凡是和script沾边的一律被删,凡是不沾边的就放行。所以这关的高效解法其实就一个:换标签。不需要在正则上死磕。

5.3 High级别的过关实操建议

实际操作的时候,点击Submit后页面会把结果回显在一个<pre>标签内,输入内容直接嵌入到Hello的文本节点里。为了保证Payload能被浏览器解析执行,这个输入点其实对Payload有一些额外要求——但这不影响我们用<img src=x onerror=alert('XSS')>直接打到弹窗。

我建议在High级别顺手做一个测试:在Chrome里右键查看页面源代码,搜索一下你的Payload字符串,看它是出现在HTML的文本节点里还是属性里。这个观察习惯对后续学DOM XSS、存储型XSS都很有帮助,能帮你快速判断Payload的构造方式是不是对的。

6. 深入理解反射型XSS的原理与危害

6.1 XSS的触发链路

反射型XSS完整跑通一次,链路是这样的:

  1. 受害者已经登录了目标网站,浏览器持有了有效的会话Cookie。
  2. 攻击者构造一个恶意URL,把JavaScript代码放在参数里,通过邮件、聊天工具发给受害者。
  3. 受害者点击链接,浏览器向目标服务器发起GET请求,服务端把参数里的恶意代码拼接进HTML页面并返回。
  4. 浏览器解析响应,执行了恶意脚本。
  5. 恶意脚本在受害者的浏览器权限下运行,能窃取Cookie、模拟用户操作、篡改页面内容。

这个链路里,最关键的一环是第3步的“拼接输出”。服务端如果对用户输入做HTML实体编码(把<变成&lt;>变成&gt;),那么用户输入就只是普通文本,浏览器不会把它解析成标签。DVWA Low级别的漏洞根源,就是它没做这一步编码。

6.2 反射型XSS能做什么

反射型XSS虽然是一次性的、需要诱导用户点击,但它的实际危害取决于业务系统的重要程度。如果目标是一个后台管理系统,攻击者可以利用反射型XSS配合其他手段盗取管理员会话,实现“登录态劫持”;如果目标是一个电商网站,攻击者可以模拟用户发起下单、改密码等请求;如果目标是一个企业邮箱系统,攻击者可以读取页面内的敏感数据甚至截取邮件内容。

从攻击者视角来看,反射型XSS通常是组合拳中的一环,很少单兵作战。比如先找一个反射型XSS,再配合短网址服务做个伪装链接,诱导用户点击,然后截获后面跳转到的业务数据。理解这一点,你就知道为什么有些企业会花大力气做XSS防护了——单看一个弹窗没什么大不了,但当你把它放在真实的业务链路上看,那就是另一回事了。

6.3 XSS、CSRF和Cookie劫持的关系

打靶场的时候很多人会遇到一个困惑:XSS和CSRF(跨站请求伪造)到底啥区别?其实两者相辅相成。XSS是利用用户对网站的信任来执行脚本,CSRF是利用网站对用户的信任来伪造请求。如果网站有XSS漏洞,攻击者完全可以借XSS构造一个CSRF的请求,实现“用用户的身份做坏事”。所以很多CSRF攻防文章里,第一步往往都是“先找一处XSS”,这两者经常成对出现。

DVWA里专门有CSRF模块,你可以等打完XSS之后再去对比着练。到时候你就会发现,反射型XSS+CSRF的组合,在很多系统里就能形成一条完整的攻击链。

7. 反射型XSS的防护:从攻击视角切换到防御视角

打靶场如果只打攻击不学防御,等于只练了矛没练盾。我们切换到开发者视角,看看DVWA的源码里给出的“标准答案”是什么样的。

7.1 Impossible级别的标准防御方案

DVWA的Impossible级别是“通关标准答案”,它给的防御措施很典型:

$name = htmlspecialchars( $_GET[ 'name' ] );

核心就这一行。htmlspecialchars函数会把HTML中的特殊字符转成实体,<变成&lt;>变成&gt;&变成&amp;"变成&quot;。经过这个函数处理后的内容,浏览器都会当成普通文本展示,不会去解析成标签。这就从根上断掉了XSS的可能。

此外,Impossible级别还加了一个token验证,防止跨站请求直接构造URL调用这个页面。这是CSRF层面的防御,和XSS防御是两层独立的事。打DVWA到Impossible级别时,可以注意看一下页面代码里多了哪些函数,每个函数起什么作用。

7.2 真实开发中的三重防护思路

在真实业务开发中,XSS防护不会只有一行代码,而是分层配合的:

  • 输入侧:对用户的输入做合法性校验,白名单只允许预期格式的数据,比如姓名只允许中文和字母,邮箱必须符合邮箱格式,数字必须是整数。
  • 输出侧:在把用户数据输出到HTML前,用htmlspecialchars或框架自带的模板转义机制做编码处理。
  • 请求侧:部署CSP(内容安全策略)响应头限制页面可以加载和执行的脚本来源,就算有XSS,CSP也能很大程度上限制恶意脚本的执行。

有一点必须强调:防御的核心是输出编码,不是输入过滤。Web安全领域有个经典案例:某个系统尝试用“过滤掉所有<script>”来防XSS,结果被<img onerror>轻松绕过——这正是你在DVWA Medium和High级别里亲身验证过的事情。所以如果你将来参与开发或代码审计,看到系统只用黑名单过滤HTML标签,要立刻意识到这条路走不通。

7.3 开发者常见的XSS防护误区

配合DVWA的练习,我想特别列出我在平时开发中常见的误区,供大家对照自查:

  • 只在Java端做转义,前端模板里直接拼接HTML字符串——等于没防。
  • 对输入做了trimaddslashes,但忘了在输出位置做转义——等于没防。
  • 用了富文本编辑器,却只过滤了<script>,没有过滤<iframe><svg>、事件属性——等于没防。
  • 以为post请求就安全了,忘记get请求和post请求都能携带恶意参数——等于没防。

这些问题在DVWA的练习里都能找到对应影子。如果你在打靶场时有过“我明明过滤了,怎么还是弹窗”的困惑,那恭喜你,你已经离真正的防护认知很近了。

8. 常见问题与排查技巧实录

打DVWA反射型XSS的时候,新手遇到的大部分问题其实不在漏洞本身,而是一些环境或操作细节。我整理了一份问题速查表:

现象可能原因解决办法
页面一直白屏PHP版本过高,代码兼容性问题降到PHP 7.4,或升级到最新DVWA版本
数据库报连接失败config.inc.php里数据库账号密码不对修改为实际MySQL账号密码,确认数据库已创建
弹窗一闪而过浏览器XSS筛选器拦截了换个本地浏览器或关掉相应安全选项,DVWA本地环境可直接忽略
Payload没弹窗安全级别没切到对应难度检查DVWA菜单右下角的Security Level设置
+号变成空格URL编码问题,GET参数里的+被解析为空格%2B代替+,或使用%20做空格
看不清参数位置不知道输入点在哪按F12打开开发者工具,切到Network看请求URL
反射内容在<pre>标签里页面回显位置在文本节点先测引用和括号,再构造带标签的Payload

最典型的一个操作问题是很多人不看URL,直接在页面输入框里敲Payload。DVWA的反射型XSS的输入会和URL绑定,点击Submit其实是在构造一个GET请求。如果你使用的是XSS测试浏览器,地址栏会原样展示Payload编码后的URL。看懂这个URL,你就能理解反射型XSS“一次请求一次响应”的本质。

还有一个容易被忽略的小坑:Chrome和Firefox内置了XSS Auditor(现在Chrome已经移除了这个机制,老版本Firefox仍然有),有时候你明明输入了正确的Payload,页面却弹不出窗,那就是浏览器在客户端层面拦截了反射型XSS。本地靶场可以让浏览器放行,或者直接换一个没有该拦截机制的测试浏览器。

9. 实操总结与后续练习建议

DVWA反射型XSS这一路打下来,从Low到High到Impossible,你其实经历了完整的“发现漏洞—利用漏洞—分析过滤—绕过过滤—理解防御”过程。这套思路框架不只在XSS上适用,在SQL注入、文件包含、命令注入等所有DVWA漏洞模块里都能复用。

接下来我建议你按照这个顺序继续练:

  • 把DVWA的XSS三个级别全部重打一遍,但这次不打开源码,纯黑盒测试,看能不能自己推算出过滤规则。
  • 对照源码阅读,把每个级别的过滤函数、输出位置、防护措施一条条记录下来。
  • 去DVWA的存储型XSS和DOM型XSS模块,对比三种XSS之间的差异。
  • 找一个合法的漏洞测试平台(比如本地搭建的其他靶场)练手,把反射型XSS的Payload在不同上下文(HTML标签内、属性内、JavaScript代码中)的写法都试一遍。
  • 自己用原生PHP写一个简单的留言板,故意不做输出编码,亲手复现一次XSS,然后再修复它。这个过程是理解XSS最有效的方法。

我个人带人入门时有一个不太一样的习惯:会故意让新手在DVWA里先“把页面玩坏”——输入各种奇怪的字符,看看输出有什么变化,然后在源码里找到对应的处理逻辑。这种“破坏式学习法”看起来进度慢,但建立的认知基础远比照着教程一步步过关要扎实得多。打靶场不是任务,而是你建立攻击者思维最好的训练场。

关于反射型XSS,最后再多说一句:这个漏洞在今天的真实互联网上依然大量存在,只是随着前端框架的普及,触发位置从服务端模板转移到了前端JavaScript渲染层。你从DVWA里学到的思路,放到现在依然有效,唯一需要更新的是Payload的写法。这就是这门手艺有意思的地方——攻击手法永远在变,但是追本溯源的思路,始终是通用的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询