合规免责声明
本文仅用于网络安全技术学习与研究,所有操作均在本地搭建的 DVWA 靶场环境中完成。XSS 攻击属于网络违法犯罪行为,未经授权对他人系统实施攻击将承担相应的法律责任。请遵守《网络安全法》等相关法律法规,切勿将本文技术用于非法用途,本文作者及发布平台不对任何非法使用行为负责。
核心认知
XSS 攻击核心定义
XSS(Cross-Site Scripting,跨站脚本攻击)是一种常见的 Web 安全漏洞,攻击者通过在 Web 页面中注入恶意 JavaScript 代码,当用户访问该页面时,恶意代码会在用户浏览器中执行,从而实现窃取 Cookie、劫持会话、伪造页面、钓鱼等恶意操作。其本质是Web 应用对用户输入的过滤不严格,导致恶意脚本被当作合法内容渲染或执行。
学习流程(零基础入门)
- 环境搭建:本地部署 DVWA 靶场(PHP+MySQL+Apache 架构),确保靶场级别可切换至 "Low/Medium",不会的可查看这篇文章:DVWA 靶场搭建:Windows11(phpstudy 搭建)(步骤 + 截图 + 常见问题)-CSDN博客;
- 原理理解:掌握 3 种 XSS 类型的核心差异(数据存储 / 传输方式、执行触发条件);
- 靶场实操:针对每种 XSS 类型,复现 Low 级别攻击,分析 Payload 生效原因;
- 代码审计:拆解 DVWA 源码,定位防护逻辑缺陷;
- 基础防御:总结入门级防御手段,理解 "输入过滤、输出编码" 的核心思想。
思维导图(文字版)

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.write、innerHTML等危险方法; - 脚本执行时机为前端 DOM 渲染阶段。
DVWA 靶场实战(Low级别)
环境准备
- 本地部署 DVWA;
- 登录 DVWA(默认账号:admin,密码:password),将靶场级别切换为 "Low"(通过左侧 "DVWA Security" 设置);
- 工具准备:Chrome/Firefox 开发者工具(审查元素、控制台)、Burp Suite 基础版(抓包分析)。
1. 反射型 XSS(Low级别)
操作步骤
- 进入 DVWA "XSS Reflected" 模块(反射型 XSS 页面);
- 在输入框中输入测试 Payload:
<script>alert('XSS')</script>;

- 点击 "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级别)
操作步骤
- 进入 DVWA "XSS Stored" 模块(存储型 XSS 页面);
- 在 "Name" 输入框输入:
test,在 "Message" 输入框输入 Payload:<script>alert('XSS')</script>;
- 点击 "Sign Guestbook" 按钮,页面立即弹出警告框,且刷新页面后弹窗仍会出现(脚本已存入数据库);

- 新建浏览器窗口登录 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();
}
?>
- 缺陷分析:
-
仅对输入做了
mysql_real_escape_string处理(防止 SQL 注入),无任何 XSS 相关过滤; -
从数据库读取数据后,直接输出到 HTML 页面,恶意脚本被渲染执行;
-
无输出编码(如
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级别)
操作步骤
- 进入 DVWA "XSS DOM" 模块(DOM 型 XSS 页面);

- 页面显示 "Please choose a language:",下方有一个输入框和 "Select" 按钮,URL 为http://localhost/dvwa/vulnerabilities/xss_d/?default=English;

- 修改 URL 中的default参数为 Payload:http://localhost/dvwa/vulnerabilities/xss_d/?default=%3Cscript%3Ealert(document.cookie)%3C/script%3E;

- 回车访问该 URL,页面弹出警告框,XSS 攻击成功;

- 打开开发者工具(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>
- 缺陷分析:
-
漏洞关键特征(DOM 型 XSS 专属):
- 全程无后端参与:恶意脚本未经过后端 PHP 处理,仅由前端 JS 读取并执行,后端仅返回静态 HTML 框架
- 双重风险放大:
decodeURI(lang)会解码攻击者的 URL 编码 payload(如%3Cscript%3Ealert(1)%3C/script%3E),让攻击更隐蔽 - 多攻击方式:除了
<script>标签,还可通过闭合option标签注入事件(如default=' onchange=alert(1) ')
-
攻击触发流程:
- 攻击者构造 URL:
http://localhost/dvwa/vulnerabilities/xss_d/?default='><script>alert(document.cookie)</script> - 受害者点击 URL 后,浏览器加载页面,前端 JS 读取 URL 中的
default参数(恶意脚本) - JS 通过
document.write将恶意脚本直接插入<select>标签内 - 浏览器解析 HTML 时执行脚本,攻击者窃取 Cookie / 控制会话
- 攻击者构造 URL:
-
基础级防御思路
1. 输入验证(前端 + 后端双重验证)
-
白名单验证:仅允许符合预期的字符(如字母、数字、指定符号),拒绝特殊字符(
<>/"/'/()); -
长度限制:限制输入内容的长度,防止超长恶意脚本注入;
-
示例(PHP):
// 仅允许字母、数字、下划线,长度1-20
name = GET['name'];
if(!preg_match('/^[a-zA-Z0-9]{1,20}/', name)) {
die('输入格式错误');
}
2. 输出编码(核心防御手段)
-
将输出到 HTML 中的特殊字符转为 HTML 实体(如
<→<,>→>,"→"),使浏览器将其视为文本而非标签; -
示例(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(如<script>alert(1)</script>),未做解码处理的场景下仍可能触发 XSS; -
CSP 基础配置仅限制同域脚本,若网站存在第三方脚本引入漏洞,攻击者可劫持第三方域名执行恶意脚本;
-
仅过滤危险标签 / 属性易被绕过(如
<scr<script>ipt>嵌套标签、onerror变形为onerrοr=(插入不可见字符))。