DVWA Medium Stored XSS安全测试与防护分析

注意:有关Stored XSS漏洞的基础知识与原理已在上篇文章指出,此处不再解释。
可参考该文章https://www.cnblogs.com/lky-sec/articles/21412117
本文仅用于网络安全学习与防御研究。所有测试均在本人本地部署、合法授权的 DVWA 靶场中进行,禁止将相关方法用于任何未授权系统。

一 测试范围与环境

本次测试目标为本地DVWA靶场中的XSS(Stored)模块。

项目 配置
测试环境 本地 DVWA 靶场
安全等级 Medium
测试系统 Kali Linux
测试工具 Firefox、Burp Suite
测试方式 黑盒测试、源码审计
授权情况 本人本地靶场,合法授权

二 页面与请求分析

1. 设置难度等级

进入 DVWA Security,将安全等级设置为 Medium,随后进入 XSS(Stored)模块。
image

2. 观察模块正常功能

页面提供 Name 和 Message 两个输入位置,用于向留言板提交用户名和留言内容。分别输入正常内容:
Name:test Message:hello
点击Sign Guestbook提交后,可以看到输入内容被保存并显示在留言板中。
image
与反射型XSS不同,Stored XSS提交的数据会被存储在服务器端,之后再次访问页面时,已经保存的内容仍会被重新输出。

3. 使用Burp Suite进行抓包分析

开启 Burp Suite Proxy 模块进行拦截,提交一次正常留言。
image
从请求中可以观察到 Name、Message以及提交按钮对应的参数。
其中:

  • txtName:用户提交的名称。
  • mtxMessage:用户提交的留言内容。
  • btnSign:表示触发留言提交操作。

三 手工黑盒测试

1. 测试Low难度Payload

在Message处输入<script>alert("x")</script> 没有弹窗,留言板内容发现有转义和<script>消失现象。
image
接下来测试Name处发现限制输入长度,利用Burp Suite进行提交<script>alert("x")</script> 没有弹窗。
image

2.尝试绕过

在Message处输入大小写混合<ScRiPt>alert("x")</ScRiPt> 未弹窗。
image
Name处输入大小写混合<ScRiPt>alert("x")</ScRiPt> 弹窗成功。
image

3.验证持久性

页面刷新重新访问页面后发现依旧弹窗。
image
image

四 黑盒测试结果分析

测试位置 测试Payload 测试结果 结果分析
Message <script>alert("x")</script> 未弹窗,标签被去除且内容发生转义 Message 参数存在较严格的 HTML 标签过滤与编码处理
Name <script>alert("x")</script> 未弹窗 小写 <script> 受到过滤,Low 难度 Payload 失效
Message <ScRiPt>alert("x")</ScRiPt> 未弹窗 大小写变化仍无法绕过 Message 参数的标签过滤
Name <ScRiPt>alert("x")</ScRiPt> 成功弹窗 Name 参数的过滤存在大小写敏感问题,可绕过过滤

五 代码审计

1. 点击view source 查看源码

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = str_replace( '<script>', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $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>' );

    //mysql_close();
}

?> 

2. 源码分析

Medium 等级首先获取 Message 和 Name 两个用户输入:

$message = trim( $_POST[ 'mtxMessage' ] );
$name    = trim( $_POST[ 'txtName' ] );

其中,Message 参数经过了较为严格的处理:

$message = strip_tags( addslashes( $message ) );
$message = mysqli_real_escape_string(...);
$message = htmlspecialchars( $message );

strip_tags() 会去除 HTML 标签,htmlspecialchars() 会对特殊字符进行 HTML 实体编码,因此 Message 中提交的<script>等 HTML 标签通常无法被浏览器作为脚本执行。

而 Name 参数只使用:

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

删除完全匹配的小写<script>字符串。该过滤方式存在大小写敏感问题,例如:<ScRiPt>alert("x")</ScRiPt>无法被匹配并删除。
随后 $name 被直接写入数据库:

$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";

当留言内容再次从数据库读取并输出到页面时,未被有效过滤的恶意标签可能被浏览器解析执行,从而形成存储型 XSS。
因此,该漏洞的主要原因是:Name 参数采用不完整的黑名单方式过滤<script>标签,且缺少可靠的HTML输出编码,导致过滤规则可以通过大小写变化等方式绕过。

3. 修复方案

1)对 Name 和 Message 参数统一进行严格的输入长度与格式限制。
2)避免使用 str_replace() 等黑名单方式过滤特定 HTML 标签。
3)对页面输出的 Name 和 Message 内容统一进行 HTML 实体编码。
4)根据数据所在的 HTML、属性或 JavaScript 上下文采用对应的输出编码方式。
5)使用参数化查询或预编译语句处理留言数据的数据库写入。
6)禁止用户可控内容直接作为 HTML 或 JavaScript 代码解析。
7)配置合理的 Content Security Policy(CSP),限制脚本执行来源。
8)对异常 XSS Payload 和高频留言请求进行日志记录与安全监控。

六 Low与Medium差异

对比项 Low Medium
参数获取方式 $_POST['mtxMessage']、$_POST['txtName'] $_POST['mtxMessage']、$_POST['txtName']
Message基础处理 trim() trim()
Message标签过滤 无 使用 strip_tags() 去除HTML标签
Message特殊字符处理 使用 stripslashes() 使用 addslashes()、htmlspecialchars()
Message数据库转义 mysqli_real_escape_string() mysqli_real_escape_string()
Name基础处理 trim() trim()
Name标签过滤 无 使用 str_replace() 删除固定小写 <script>
Name数据库转义 mysqli_real_escape_string() mysqli_real_escape_string()
HTML输出编码 无 Message进行了 htmlspecialchars() 处理,Name未进行
<script>在Message中 可形成XSS 被 strip_tags() 去除
大小写混合标签在Message中 可形成XSS 仍会被 strip_tags() 去除
<script>在Name中 可形成XSS 固定小写形式被过滤
大小写混合标签在Name中 可形成XSS 可绕过 str_replace() 过滤
存储型XSS漏洞 存在 仍然存在
主要防护措施 仅进行数据库相关转义,缺少XSS防护 Message增加标签过滤和HTML编码,Name增加固定字符串过滤
主要安全问题 用户输入缺少有效HTML安全处理 Name参数黑名单过滤不完整,且缺少可靠的输出编码
根本修复方式 对输出内容进行上下文相关编码 对所有用户可控输出统一进行上下文相关编码,避免依赖黑名单过滤
posted @ 2026-07-27 17:19  网安新手  阅读(10)  评论(0)    收藏  举报