XSS 跨站脚本攻击:3 种类型(存储型 / 反射型 / DOM 型)原理以 DVWA 靶场举例

合规免责声明

本文仅用于网络安全技术学习与研究,所有操作均在本地搭建的 DVWA 靶场环境中完成。XSS 攻击属于网络违法犯罪行为,未经授权对他人系统实施攻击将承担相应的法律责任。请遵守《网络安全法》等相关法律法规,切勿将本文技术用于非法用途,本文作者及发布平台不对任何非法使用行为负责。

核心认知

XSS 攻击核心定义

XSS(Cross-Site Scripting,跨站脚本攻击)是一种常见的 Web 安全漏洞,攻击者通过在 Web 页面中注入恶意 JavaScript 代码,当用户访问该页面时,恶意代码会在用户浏览器中执行,从而实现窃取 Cookie、劫持会话、伪造页面、钓鱼等恶意操作。其本质是Web 应用对用户输入的过滤不严格,导致恶意脚本被当作合法内容渲染或执行。

学习流程(零基础入门)

  1. 环境搭建:本地部署 DVWA 靶场(PHP+MySQL+Apache 架构),确保靶场级别可切换至 "Low/Medium",不会的可查看这篇文章:DVWA 靶场搭建:Windows11(phpstudy 搭建)(步骤 + 截图 + 常见问题)-CSDN博客
  2. 原理理解:掌握 3 种 XSS 类型的核心差异(数据存储 / 传输方式、执行触发条件);
  3. 靶场实操:针对每种 XSS 类型,复现 Low 级别攻击,分析 Payload 生效原因;
  4. 代码审计:拆解 DVWA 源码,定位防护逻辑缺陷;
  5. 基础防御:总结入门级防御手段,理解 "输入过滤、输出编码" 的核心思想。

思维导图(文字版)

3 种 XSS 原理拆解(附攻击链路图)

1. 反射型 XSS(非持久型 XSS)

核心原理

反射型 XSS 是最常见的 XSS 类型,恶意脚本通过 URL 参数、表单提交等方式传递给 Web 服务器,服务器未对输入做有效过滤,直接将恶意脚本 "反射" 回用户浏览器,脚本仅在当前请求中执行,无持久化存储。

攻击链路图
关键特征
  • 一次性攻击,刷新页面后脚本失效;
  • 依赖用户点击恶意链接,攻击范围有限;
  • 数据不经过数据库存储,仅在 HTTP 请求 / 响应中传递。

2. 存储型 XSS(持久型 XSS)

核心原理

存储型 XSS 是危害最大的 XSS 类型,攻击者将恶意脚本提交至 Web 应用的数据库(如留言板、评论区、用户资料),当其他用户访问包含该脚本的页面时,服务器从数据库中读取恶意脚本并返回给用户浏览器,脚本在所有访问该页面的用户端执行。

攻击链路图
关键特征
  • 持久化攻击,脚本永久存储在数据库中;
  • 无需诱导用户点击链接,只要访问受影响页面即触发;
  • 攻击范围广,可影响所有访问该页面的用户。

3. DOM 型 XSS

核心原理

DOM 型 XSS 与前两种类型不同,其攻击发生在前端 DOM(文档对象模型)操作中,无需后端服务器参与。攻击者构造恶意输入,前端 JavaScript 在处理 URL 参数、表单值等数据时,未对输入做过滤,直接将数据插入 DOM 树中,导致恶意脚本执行。

攻击链路图
关键特征
  • 纯前端攻击,后端服务器无感知(响应包中无恶意脚本);
  • 依赖前端 DOM 操作的漏洞,如document.writeinnerHTML等危险方法;
  • 脚本执行时机为前端 DOM 渲染阶段。

DVWA 靶场实战(Low级别)

环境准备

  1. 本地部署 DVWA;
  2. 登录 DVWA(默认账号:admin,密码:password),将靶场级别切换为 "Low"(通过左侧 "DVWA Security" 设置);
  3. 工具准备:Chrome/Firefox 开发者工具(审查元素、控制台)、Burp Suite 基础版(抓包分析)。

1. 反射型 XSS(Low级别)

操作步骤
  1. 进入 DVWA "XSS Reflected" 模块(反射型 XSS 页面);
  2. 在输入框中输入测试 Payload:<script>alert('XSS')</script>
  3. 点击 "Submit" 按钮,页面弹出警告框,说明 XSS 攻击成功。
Payload 说明:
  • 核心 Payload:<script>alert('XSS')</script>(最基础的 JavaScript 弹窗脚本);
  • 生效原因:Low 级别下,DVWA 未对输入做任何过滤,输入的<script>标签直接被渲染到 HTML 页面中,浏览器解析时执行其中的 JS 代码。
代码审计

打开 DVWA 源码目录下的vulnerabilities/xss_r/source/low.php,核心代码如下:

复制代码
<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
	// Feedback for end user
	$html .= '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}

?>
  • 缺陷分析:代码直接获取$_GET['name']参数,未做任何过滤(如过滤<script>标签、转义特殊字符),直接通过echo输出到 HTML 中,导致输入的恶意脚本被执行。

  • 详情如下:

    复制代码
    <?php
    // 核心风险1:禁用浏览器内置的XSS防护机制
    // X-XSS-Protection: 0 会关闭Chrome、IE等浏览器的内置XSS过滤器,
    // 即使浏览器检测到潜在XSS攻击,也不会拦截,相当于主动移除了一层基础防护
    header ("X-XSS-Protection: 0");
    
    // Is there any input?
    // 检查GET请求中是否存在"name"参数,且参数值非空
    if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
        // Feedback for end user
        // 核心风险2:用户输入未过滤、未编码,直接拼接至HTML输出
        // 1. $_GET['name'] 是用户可控的输入,攻击者可构造任意内容(如<script>alert('xss')</script>)
        // 2. 直接将用户输入拼接到<pre>标签内,浏览器解析HTML时会执行注入的脚本标签/事件
        // 3. 无任何转义(如htmlspecialchars)或过滤操作,属于典型的反射型XSS漏洞
        $html .= '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
    }

2. 存储型 XSS(Low级别)

操作步骤
  1. 进入 DVWA "XSS Stored" 模块(存储型 XSS 页面);
  2. 在 "Name" 输入框输入:test,在 "Message" 输入框输入 Payload:<script>alert('XSS')</script>
  3. 点击 "Sign Guestbook" 按钮,页面立即弹出警告框,且刷新页面后弹窗仍会出现(脚本已存入数据库);
  4. 新建浏览器窗口登录 DVWA或者刷新原页面,访问该页面,仍会触发弹窗,验证持久化特征。
Payload说明
  • 核心 Payload:<script>alert('XSS')</script>
  • 生效原因:Low 级别下,应用未对留言内容做任何过滤,直接将内容存入数据库,每次加载留言板页面时,从数据库读取脚本并输出到 HTML 中,所有访问者均会触发。
代码审计

打开vulnerabilities/xss_s/source/low.php,核心代码如下:

复制代码
<?php

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

	// Sanitize message input
	$message = stripslashes( $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)) ? "" : ""));

	// Sanitize name input
	$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();
}

?>
  • 缺陷分析:
    1. 仅对输入做了mysql_real_escape_string处理(防止 SQL 注入),无任何 XSS 相关过滤;

    2. 从数据库读取数据后,直接输出到 HTML 页面,恶意脚本被渲染执行;

    3. 无输出编码(如htmlspecialchars),无法将特殊字符(<>/"/')转为 HTML 实体。

      复制代码
      <?php
      
      if( isset( $_POST[ 'btnSign' ] ) ) {
          // Get input
          // 仅做了首尾空格去除,未对XSS相关特殊字符做任何过滤/编码
          $message = trim( $_POST[ 'mtxMessage' ] ); // 接收用户提交的评论内容(核心用户输入点)
          $name    = trim( $_POST[ 'txtName' ] );    // 接收用户提交的姓名(次要用户输入点)
      
          // Sanitize message input
          $message = stripslashes( $message ); // 仅去除反斜杠,对XSS无防护作用
          // 核心误区1:仅做MySQL防注入处理,未做XSS防护
          // mysqli_real_escape_string的作用是转义SQL语句中的特殊字符(如单引号、双引号),
          // 仅能防止SQL注入,无法过滤/转义HTML特殊字符(如<、>、script标签),对XSS无效
          $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)) ? "" : ""));
      
          // Sanitize name input
          // 同理:仅防SQL注入,不防XSS
          $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
          // 核心风险2:未过滤的用户输入被存入数据库(存储型XSS关键环节)
          // 攻击者注入的<script>等恶意代码会被持久化存储到guestbook表中,
          // 所有访问该评论页面的用户都会加载此恶意代码
          $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();
          
          // 核心风险3:代码仅处理"存入数据库"环节,未考虑"从数据库读取输出"环节的编码
          // 即使存入时未过滤,若读取输出时对内容做HTML编码也可防御XSS,但此代码无任何输出编码逻辑,
          // 当页面从数据库读取comment/name并渲染时,恶意脚本会直接被浏览器执行
      }
      ?>

3. DOM 型 XSS(Low级别)

操作步骤
  1. 进入 DVWA "XSS DOM" 模块(DOM 型 XSS 页面);
  2. 页面显示 "Please choose a language:",下方有一个输入框和 "Select" 按钮,URL 为http://localhost/dvwa/vulnerabilities/xss_d/?default=English
  3. 修改 URL 中的default参数为 Payload:http://localhost/dvwa/vulnerabilities/xss_d/?default=%3Cscript%3Ealert(document.cookie)%3C/script%3E
  4. 回车访问该 URL,页面弹出警告框,XSS 攻击成功;
  5. 打开开发者工具(F12),查看 Elements 面板,发现<option value="%3Cscript%3Ealert(document.cookie)%3C/script%3E"><script>alert(document.cookie)</script></option>,恶意脚本已插入 DOM 中。
Payload说明:
  • 核心 Payload:<script>alert(document.cookie)</script>
  • 生效原因:Low 级别下,前端 JS 直接读取 URL 中的name参数,未做任何过滤,将其插入 DOM 树中,浏览器解析 DOM 时执行脚本。
代码审计

打开在xss_dom型的漏洞页面点击鼠标右键查看源代码,这个漏洞在前端的,后端没有代码:

复制代码
<!DOCTYPE html>
<html lang="en-GB">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Vulnerability: DOM Based Cross Site Scripting (XSS) :: Damn Vulnerable Web Application (DVWA)</title>
    <link rel="stylesheet" type="text/css" href="../../dvwa/css/main.css" />
    <link rel="icon" type="\image/ico" href="../../favicon.ico" />
    <script type="text/javascript" src="../../dvwa/js/dvwaPage.js"></script>
</head>
<body class="home">
    <div id="container">
        <!-- 省略导航/布局代码(无漏洞,无需分析) -->
        <div id="main_body">
            <div class="body_padded">
                <h1>Vulnerability: DOM Based Cross Site Scripting (XSS)</h1>
                <div class="vulnerable_code_area">
                    <p>Please choose a language:</p>
                    <form name="XSS" method="GET">
                        <select name="default">
                            <script>
                                // 漏洞触发条件:URL中包含"default="参数(攻击者可任意构造)
                                if (document.location.href.indexOf("default=") >= 0) {
                                    // 风险1:未过滤读取URL参数
                                    // 从URL中截取"default="后的所有内容,攻击者可传入任意字符(如<script>)
                                    // 例:URL为?default=<script>alert(1)</script>,则lang的值就是"<script>alert(1)</script>"
                                    var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
                                    
                                    // 风险2:document.write直接拼接未过滤参数到HTML,核心XSS漏洞
                                    // 1. "value='" + lang + "'":若lang包含单引号(如' onmouseover=alert(1) '),会闭合option标签的value属性,注入事件
                                    // 2. decodeURI(lang):解码URL编码的字符(如%3C转为<),进一步放大攻击面
                                    // 3. 无任何HTML编码(如转义<、>、'),恶意代码直接被浏览器解析执行
                                    document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
                                    document.write("<option value='' disabled='disabled'>----</option>");
                                }
                                // 以下是正常语言选项,无风险
                                document.write("<option value='English'>English</option>");
                                document.write("<option value='French'>French</option>");
                                document.write("<option value='Spanish'>Spanish</option>");
                                document.write("<option value='German'>German</option>");
                            </script>
                        </select>
                        <input type="submit" value="Select" />
                    </form>
                </div>
                <!-- 省略更多信息链接(无漏洞) -->
            </div>
        </div>
        <!-- 省略底部布局代码(无漏洞) -->
    </div>
</body>
</html>
  • 缺陷分析:
    1. 漏洞关键特征(DOM 型 XSS 专属):

      • 全程无后端参与:恶意脚本未经过后端 PHP 处理,仅由前端 JS 读取并执行,后端仅返回静态 HTML 框架
      • 双重风险放大:decodeURI(lang)会解码攻击者的 URL 编码 payload(如%3Cscript%3Ealert(1)%3C/script%3E),让攻击更隐蔽
      • 多攻击方式:除了<script>标签,还可通过闭合option标签注入事件(如default=' onchange=alert(1) '
    2. 攻击触发流程

      • 攻击者构造 URL:http://localhost/dvwa/vulnerabilities/xss_d/?default='><script>alert(document.cookie)</script>
      • 受害者点击 URL 后,浏览器加载页面,前端 JS 读取 URL 中的default参数(恶意脚本)
      • JS 通过document.write将恶意脚本直接插入<select>标签内
      • 浏览器解析 HTML 时执行脚本,攻击者窃取 Cookie / 控制会话

基础级防御思路

1. 输入验证(前端 + 后端双重验证)

  • 白名单验证:仅允许符合预期的字符(如字母、数字、指定符号),拒绝特殊字符(<>/"/'/());

  • 长度限制:限制输入内容的长度,防止超长恶意脚本注入;

  • 示例(PHP):

    // 仅允许字母、数字、下划线,长度1-20
    name = GET['name'];
    if(!preg_match('/^[a-zA-Z0-9
    ]{1,20}/', name)) {
    die('输入格式错误');
    }

2. 输出编码(核心防御手段)

  • 将输出到 HTML 中的特殊字符转为 HTML 实体(如<&lt;>&gt;"&quot;),使浏览器将其视为文本而非标签;

  • 示例(PHP):使用htmlspecialchars函数编码输出:

    name = _GET['name'];
    // ENT_QUOTES:同时转义单引号和双引号
    echo "

    Hello " . htmlspecialchars($name, ENT_QUOTES) . "
    ";

  • 前端防御:使用textContent替代innerHTML插入文本(textContent仅渲染文本,不解析 HTML):

    var name = document.location.href.match(/name=([^&]+)/)[1];
    document.getElementById('greeter').textContent = 'Hello ' + name;

3. 禁用危险标签 / 属性

  • 后端过滤常见的 XSS 危险标签(<script>/<img>/<iframe>)和危险属性(onerror/onclick/onload);
  • 注意:仅过滤无法完全防御,需配合输出编码使用。

4. 基础 CSP(内容安全策略)

  • 通过 HTTP 响应头Content-Security-Policy限制脚本执行来源,禁止内联脚本、eval 函数等;

  • 示例(Apache 配置):

    Header set Content-Security-Policy "default-src 'self'; script-src 'self';"

  • 效果:仅允许加载同域的脚本,禁止内联<script>标签和eval执行,大幅降低 XSS 风险。

    补充说明:基础防御手段的局限性

    需要明确的是,上述基础防御思路仅能抵御初级 XSS 攻击,针对中级 / 高级绕过手段(如 Unicode 编码、HTML 实体混淆、事件属性变形等)仍存在不足,这也是后续付费专栏将深入讲解的核心内容。例如:

  • htmlspecialchars 仅能转义基础特殊字符,若攻击者将脚本编码为 Unicode(如&#x3c;script&#x3e;alert(1)&#x3c;/script&#x3e;),未做解码处理的场景下仍可能触发 XSS;

  • CSP 基础配置仅限制同域脚本,若网站存在第三方脚本引入漏洞,攻击者可劫持第三方域名执行恶意脚本;

  • 仅过滤危险标签 / 属性易被绕过(如<scr<script>ipt>嵌套标签、onerror变形为onerrοr=(插入不可见字符))。

相关推荐
代码猎人8 小时前
CSS可继承属性和不可继承属性有哪些
前端
用户44783153602328 小时前
基于 vue3 完成动态组件库建设
前端
xhxxx8 小时前
Vite + React 黄金组合:打造秒开、可维护、高性能的现代前端工程
前端·react.js·vite
用户8168694747258 小时前
深入 useState、useEffect 的底层实现
前端·react.js
Tzarevich8 小时前
React 中的 JSX 与组件化开发:以函数为单位构建现代前端应用
前端·react.js·面试
李香兰lxl8 小时前
A I时代如何在研发团队中展现「前端」的魅力
前端
本末倒置1838 小时前
解决 vue2.7使用 pnpm 和 pinia 2.x报错
前端
CoderLiz8 小时前
Flutter中App升级实现
前端
Mintopia8 小时前
⚛️ React 17 vs React 18:Lanes 是同一个模型,但跑法不一样
前端·react.js·架构