DVWA存储型XSS攻防实战:从原理到绕过与防御

发布时间:2026/7/4 17:18:40
DVWA存储型XSS攻防实战:从原理到绕过与防御 1. 项目概述从靶场实战到存储型XSS的深度剖析如果你正在学习网络安全尤其是Web安全那么DVWADamn Vulnerable Web Application这个靶场你一定不陌生。它就像是一个专门为安全爱好者搭建的“练功房”里面包含了SQL注入、文件上传、XSS等十几种常见的Web漏洞。今天我们不谈别的就聚焦在其中一个既经典又极具危害性的漏洞上存储型XSSStored Cross-Site Scripting。为什么单独拎出来讲因为相比反射型XSS存储型XSS的危害等级要高得多。反射型XSS就像一把需要你亲手递给攻击者的刀攻击链依赖用户点击一个精心构造的恶意链接。而存储型XSS则像是攻击者提前在网站的水源里下了毒所有后续来喝水的用户都会中招攻击是持久且广泛的。DVWA的XSSStored模块就完美模拟了这种场景一个简单的留言板或评论系统用户输入的内容被永久存储在服务器数据库里并在其他用户访问页面时被加载执行。网上有很多“DVWA通关教程”告诉你每一步点哪里、输入什么Payload。但这远远不够。知其然更要知其所以然。这篇文章的目的就是带你超越简单的“通关”深入理解每一次攻击背后的代码逻辑、防御机制的演变以及我们作为攻击者或防御者的思考过程。我们会从Low级别毫无防护的“裸奔”状态开始一步步闯过Medium、High级别的防线最终分析Impossible级别是如何从根本上杜绝漏洞的。在这个过程中你会掌握XSS Payload的构造技巧、前端的绕过思路以及最重要的——建立一种面对黑盒目标时如何系统性地测试和挖掘XSS漏洞的思维模型。2. 靶场环境与攻击核心原理拆解2.1 DVWA环境搭建与核心模块定位在开始“攻击”之前我们必须先有一个稳定、可控的“战场”。DVWA的搭建并不复杂通常需要一个集成了Apache、MySQL、PHP的环境比如XAMPP、PHPStudy或直接使用Kali Linux自带的LAMP堆栈。这里不赘述详细的安装步骤但强调几个关键点这些点往往是被新手忽略的“坑”PHP版本兼容性DVWA对PHP版本有一定要求建议使用PHP 5.4 - 7.x的版本。PHP 8.x可能会导致部分功能异常。在config/config.inc.php文件中你需要正确配置数据库连接信息。一个常见的错误是MySQL密码为空或使用了错误的数据库名默认是dvwa。安全等级设置登录DVWA后默认账号admin/password务必在左侧“DVWA Security”选项卡中将安全级别设置为Low、Medium、High或Impossible。这个设置会全局影响所有漏洞模块的服务器端代码是我们进行分级挑战的基础。练习时请从Low开始逐步提升。XSSStored模块入口在左侧导航栏找到“XSSStored”。这个模块模拟了一个简单的留言板。页面通常包含两个部分一个表单让你输入“Name”和“Message”和一个下方显示所有历史留言的区域。你的攻击目标就是通过这个表单注入恶意脚本并让它持久化存储在他人查看留言板时自动执行。注意强烈建议在虚拟机或隔离的网络环境中搭建和练习DVWA。虽然它是故意设计为有漏洞的但错误的配置或随意的网络暴露仍可能带来不必要的风险。2.2 存储型XSS攻击原理深度解析要发动有效的攻击必须彻底理解攻击的原理。存储型XSS的攻击链可以清晰地分为四个阶段输入注入攻击者在一个允许用户提交内容并存储的功能点如留言、评论、个人简介、商品评价中提交一段包含恶意JavaScript代码的输入。这段输入不仅仅是简单的文本而是被精心构造的Payload。持久化存储服务器端程序在没有进行充分过滤和验证的情况下直接将用户输入包含Payload存入数据库。这是漏洞形成的根本原因。恶意代码加载当其他正常用户受害者访问包含这些存储数据的页面时例如查看留言板Web应用程序会从数据库中读取数据并将其作为页面内容的一部分通常是HTML的一部分发送到受害者的浏览器。客户端执行受害者的浏览器接收到服务器响应将其渲染为HTML。由于Payload被当作合法的页面内容浏览器会忠实地执行其中的JavaScript代码。至此攻击完成。关键在于第3和第4步代码的执行环境是受害者的浏览器且其权限与目标网站当前会话的权限相同。这意味着恶意脚本可以盗取Cookie通过document.cookie获取用户的会话标识从而劫持用户账户。模拟用户操作发起网络请求如转账、改密、发帖即CSRF攻击。钓鱼在页面中伪造登录框诱骗用户输入敏感信息。破坏页面篡改页面内容进行挂马或跳转到恶意网站。理解这个原理后我们就能明白防御的核心思路在数据存储前输入验证/过滤或数据输出前输出编码进行严格处理确保用户输入的数据始终被当作“文本”来处理而不是可以被浏览器解析执行的“代码”。3. 分级攻击实战从Low到High的攻防博弈接下来我们进入实战环节。我们将安全级别从Low逐步调到High观察防御是如何层层加码的而我们作为攻击者又该如何见招拆招。3.1 Low级别毫无防护的“裸奔”状态将DVWA安全级别设置为Low进入XSSStored模块。攻击过程与Payload分析在Name或Message输入框中直接输入经典的XSS测试Payloadscriptalert(XSS)/script。提交后你会发现页面弹出了警告框并且这条“留言”被成功保存。刷新页面或新开一个浏览器访问该页面警告框依然会弹出。这说明我们的脚本被原封不动地存储并输出了。背后的代码逻辑源码分析查看Low级别的服务端源码通常位于vulnerabilities/xss_stored/source/low.php关键部分如下?php if( isset( $_POST[ btnSign ] ) ) { // 获取输入 $message trim( $_POST[ mtxMessage ] ); $name trim( $_POST[ txtName ] ); // 直接存入数据库没有任何过滤 $query INSERT INTO guestbook ( comment, name ) VALUES ( $message, $name );; $result mysqli_query($GLOBALS[___mysqli_ston], $query ) or die( pre . ((is_object($GLOBALS[___mysqli_ston])) ? mysqli_error($GLOBALS[___mysqli_ston]) : (($___mysqli_res mysqli_connect_error()) ? $___mysqli_res : false)) . /pre ); } ?以及输出部分的代码?php // 从数据库查询留言 $query SELECT * FROM guestbook;; $result mysqli_query($GLOBALS[___mysqli_ston], $query ) or die( pre . ((is_object($GLOBALS[___mysqli_ston])) ? mysqli_error($GLOBALS[___mysqli_ston]) : (($___mysqli_res mysqli_connect_error()) ? $___mysqli_res : false)) . /pre ); while($row mysqli_fetch_assoc($result)) { // 直接输出没有任何编码 echo div class\message\; echo pstrong{$row[name]}/strong: {$row[comment]}/p; echo /div; } ?漏洞根因代码对用户输入$message和$name没有进行任何过滤或编码直接拼接SQL语句存入数据库又从数据库直接取出拼接进HTML页面。我们的script标签被浏览器当成了合法的HTML标签解析并执行。实操心得 在Low级别你可以尝试各种基础的XSS Payload理解不同上下文Name输入框通常输出在strong标签内属于HTML标签内部HTML上下文。Message输入框输出在p标签内也是HTML上下文。 你可以测试img srcx onerroralert(1)、svg onloadalert(1)等它们都能成功。这里的关键是建立“输入点-输出点”的对应关系并确认输出上下文。3.2 Medium级别初级的过滤与绕过将安全级别调整为Medium再次尝试之前的Payloadscriptalert(XSS)/script。你会发现提交后页面没有弹窗Payload似乎被“吃掉”了。防御机制分析查看Medium级别的源码medium.php关键过滤代码如下?php if( isset( $_POST[ btnSign ] ) ) { $message trim( $_POST[ mtxMessage ] ); $name trim( $_POST[ txtName ] ); // 对$message和$name进行了简单的字符串替换过滤 $message strip_tags( addslashes( $message ) ); $name str_replace( script, , $name ); $name str_replace( /script, , $name ); // 存入数据库 $query INSERT INTO guestbook ( comment, name ) VALUES ( $message, $name );; $result mysqli_query($GLOBALS[___mysqli_ston], $query ) or die( pre . ((is_object($GLOBALS[___mysqli_ston])) ? mysqli_error($GLOBALS[___mysqli_ston]) : (($___mysqli_res mysqli_connect_error()) ? $___mysqli_res : false)) . /pre ); } ?代码解读strip_tags()这是一个PHP函数用于剥去字符串中的HTML和PHP标签。它被用在了$message上。这意味着如果你在Message框里输入scriptalert(1)/scriptstrip_tags()会直接移除script和/script标签只留下alert(1)文本从而失效。str_replace(‘script’, ‘’, $name)这是一个非常初级、存在明显缺陷的黑名单过滤。它仅将字符串“