20232417 2025-2026-1 《网络与系统攻防技术》实验八实验报告
1.实验内容
1.1实验目的
本实验旨在通过构建一个具备基础交互功能的Web应用,并对其进行多种常见网络安全攻击的模拟与实践,达到以下目标:
1.掌握基础Web技术栈开发流程:理解并实践从前端(HTML、JavaScript)到后端(PHP、MySQL)再到服务器(Apache)的完整Web应用搭建过程。
2.理解前后端交互与数据安全的重要性:通过编写用户登录和认证模块,深刻体会数据在前后端传输、验证、处理和存储的关键环节。
3.深入理解Web核心安全漏洞及其原理:通过主动对自建网站实施攻击(HTML注入、JavaScript注入、SQL注入、XSS),直观感受漏洞的产生条件、利用方式和潜在危害。
4.扩展学习复杂攻击场景:借助成熟的漏洞教学平台(WebGoat),在更贴近真实且安全的模拟环境中,系统性学习和演练SQL注入、XSS、CSRF等高级攻击技术与防御策略。
1.2学习内容
本次学习内容包括以下方面:在技术层面,需要掌握使用HTML和JavaScript构建交互式前端页面,利用PHP处理逻辑并连接MySQL数据库进行用户认证,同时熟悉Apache服务器的基本配置与使用。在安全层面,核心在于深入理解三类主要Web漏洞:一是各类注入攻击,包括通过操纵输入以篡改网页结构的HTML注入、欺骗数据库执行恶意命令的SQL注入,以及注入脚本代码的JavaScript攻击;二是跨站脚本攻击,即如何使恶意脚本在用户浏览器中执行以窃取信息或劫持会话;三是跨站请求伪造攻击,学习攻击者如何利用用户的登录状态诱导其执行非意愿操作。整个学习过程通过亲手开发应用并对其进行攻击渗透,以及使用WebGoat平台进行专项演练来完成。
2.实验过程
2.1Web前端HTML
2.1.1初步了解HTML和Apache
- HTML是构建网页的标准标记语言,用于定义网页的结构和内容。
- 表单是用户与网站交互的重要组件,用于收集用户输入。
- GET方法将数据附加在URL的查询字符串中,就像在地址后面加上一个问号和一系列参数。当你使用浏览器进行搜索、点击链接或访问一个带参数的网页时,通常都是在使用GET请求。这种方式使得数据完全可见——任何人都能看到URL中的参数,也能被浏览器缓存、收藏或分享。正是因为数据暴露在URL中,GET有长度限制,且不适合传输敏感信息如密码。GET请求主要用于获取数据而非修改数据。
- POST方法则采取了更隐蔽的方式。它将数据封装在HTTP请求的"身体"部分,用户看不见这些数据,也不会出现在浏览器的地址栏中。当你提交登录表单、上传文件或进行任何可能改变服务器状态的操作时,应该使用POST方法。由于数据不在URL中,POST没有严格的数据长度限制,能够传输大量数据,包括二进制文件。POST请求不是幂等的——多次提交同一个表单可能会导致重复创建资源(比如重复下单),因此浏览器通常会警告用户是否要重新提交POST表单。
由于Kali虚拟机上已经安装了Apache,因此输入systemctl start apache2命令就可以打开Apache,再使用systemctl status apache2.service命令查看服务器状态。从图上可以看见Apache正在运行中。
Apache(全称Apache HTTP Server)是一款开源的、跨平台的HTTP服务器软件,也是全球使用最广泛的Web服务器之一。简单说,它的核心作用是:接收用户浏览器的HTTP请求,返回对应的网页(HTML、PHP、静态资源等),是Web服务的 “基石”。

当你在kali虚拟机自带的火狐浏览器中输入localhost,打开之后是Apache的界面,说明Apache启动成功。

2.1.2编程实现HTML表单功能
在虚拟机输入cd /var/www/html进入这个/var/www/html目录下,使用vim编辑器创建html文件。

点击查看代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户登录</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.login-container {
width: 100%;
max-width: 400px;
padding: 20px;
}
.login-box {
background: white;
border-radius: 10px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
padding: 40px;
}
.login-header {
text-align: center;
margin-bottom: 30px;
}
.login-header h1 {
color: #333;
font-size: 28px;
margin-bottom: 5px;
}
.form-group {
margin-bottom: 25px;
}
.form-group label {
display: block;
color: #555;
margin-bottom: 8px;
font-weight: 600;
}
.form-group input {
width: 100%;
padding: 12px 15px;
border: 2px solid #e1e1e1;
border-radius: 6px;
font-size: 16px;
transition: border-color 0.3s ease;
}
.form-group input:focus {
outline: none;
border-color: #667eea;
}
.login-btn {
width: 100%;
padding: 12px;
background: linear-gradient(to right, #667eea, #764ba2);
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease;
}
.login-btn:hover {
transform: translateY(-2px);
}
.login-btn:active {
transform: translateY(0);
}
</style>
</head>
<body>
<div class="login-container">
<div class="login-box">
<div class="login-header">
<h1>用户登录</h1>
</div>
<form id="loginForm" action="login.php" method="POST">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username"
placeholder="请输入用户名" required autofocus>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password"
placeholder="请输入密码" required>
</div>
<button type="submit" class="login-btn">
登录
</button>
</form>
</div>
</div>
<script>
// 简单的前端验证
document.getElementById('loginForm').addEventListener('submit', function(e) {
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value;
// 验证用户名
if (username.length < 3) {
alert('用户名至少需要3个字符');
e.preventDefault();
return false;
}
// 验证密码
if (password.length < 6) {
alert('密码至少需要6个字符');
e.preventDefault();
return false;
}
return true;
});
</script>
</body>
</html>
当使用POST方法的时候,当用户点击登录按钮时,用户名和密码数据将通过HTTP请求体发送不会出现在URL地址栏中,是处理敏感信息(如密码)的正确方式。

实现的效果如图:

可以看到用户名和密码不会出现在URL中。
当使用GET方法的时候,相关信息则会出现在URL中。
代码中GET方法的体现:

效果如图:

2.2Web前端javascipt
2.2.1JavaScript表单功能实现
这是一个纯前端的登录验证系统,当我们输入用户名和密码时,JavaScript会实时检查格式是否正确——用户名必须是3-20位的字母、数字或下划线,密码需要6-20位且包含字母和数字。输入正确会显示绿色边框,错误则显示红色边框和提示信息。当点击登录后,如果验证通过,页面会动态显示“欢迎+用户名”的欢迎信息,同时登录按钮会暂时变为“已登录”状态并禁用,防止重复点击,3秒后自动恢复。整个过程不需要刷新页面,完全由JavaScript控制DOM元素的显示和样式变化。代码还支持键盘操作,按Enter键可直接触发登录。所有DOM操作过程都会在浏览器控制台显示,方便查看JavaScript如何获取和修改页面元素。这是一个典型的前端交互示例,展示了JavaScript如何实现表单验证和动态页面更新。
添加的代码如下:
点击查看代码
<script>
function submit_login() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
var usernameRegex = /^.{4,}$/;
var passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,}$/;
if (!usernameRegex.test(username)) {
alert('用户名必须在4个字符以上');
return;
}
if (!passwordRegex.test(password)) {
alert('密码必须是6个字符以上,包含至少一个字母和一个数字');
return;
}
document.write('欢迎 ' + username);
};
</script>


2.2.2注入攻击
由于之前.html对与用户的输入有限制,用户名与密码的组成只能为字母和数字,这样的情况下较难实现注入攻击,所以修改了.html之后,成功实现了注入攻击。
在登录的用户名框内输入<u>HTML injection succeed.</u>,这样登录界面会回显带下划线的HTML injection succeed.。

在用户名框内输入<img src='x' onerror='document.body.innerHTML = "JavaScript injection succeed."'>,这段代码故意设置了一个无效的图片路径,以及图片加载失败的时候会触发的事件:即将网页内容替换为恶意代码(JavaScript injection succeed.)。

2.3Web后端:MySQL基础
在kali虚拟机中输入systemctl start mysql就可以启动虚拟机自带的MYSQL,再输入mysql就可以进入数据库。

输入以下内容创建一个数据库,
点击查看代码
-- 创建数据库
CREATE DATABASE 20232417db;
-- 使用数据库
USE 20232417db;
-- 创建用户
CREATE USER 'user20232417' IDENTIFIED BY '20232417pdj';
-- 授予用户权限
GRANT ALL PRIVILEGES ON 20232417db.* TO 'user20232417';
FLUSH PRIVILEGES;
-- 修改用户密码
ALTER USER 'user20232418' IDENTIFIED BY '20232417pdj!';
-- 创建表
CREATE TABLE onetable (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
pwd VARCHAR(255) NOT NULL
);
-- 插入数据
INSERT INTO onetable (name, pwd) VALUES ('20232417pdj', '20232417pdj');
-- 查看表中所有内容
SELECT * FROM onetable;

2.4Web后端:编写PHP网页
撰写一个PHP代码,它首先建立与MySQL数据库的连接,然后接收用户通过表单提交的用户名和密码。代码会查询数据库中的用户表,先检查输入的用户名是否存在,如果存在就进一步验证密码是否正确。根据验证结果,它会输出相应的提示信息,比如“欢迎登录成功”、“密码错误”或“没有该用户”。整个流程模拟了网站登录的基本功能。
点击查看代码
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 数据库连接信息
$host = 'localhost';
$dbname = '20232417db';
$user = 'user20232417';
$password = 'pdj20232417'; // 你刚设置的密码
echo "<h3>数据库连接测试</h3>";
// 创建数据库连接
$conn = new mysqli($host, $user, $password, $dbname);
// 检查连接是否成功
if ($conn->connect_error) {
die("数据库连接失败: " . $conn->connect_error);
} else {
echo "✓ 数据库连接成功!<br><br>";
}
// 显示登录表单或处理登录
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username'])) {
// 处理登录请求
$username = trim($_POST['username']);
$password_input = trim($_POST['password']);
echo "正在验证用户: " . htmlspecialchars($username) . "<br>";
// 安全方式:使用预处理语句
$stmt = $conn->prepare("SELECT id, name FROM onetable WHERE name = ? AND pwd = ?");
$stmt->bind_param("ss", $username, $password_input);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$user_data = $result->fetch_assoc();
echo "<h2 style='color:green;'>✓ 欢迎登录成功: " . htmlspecialchars($user_data['name']) . "!</h2>";
echo "用户ID: " . $user_data['id'];
} else {
echo "<h2 style='color:red;'>✗ 用户名或密码错误</h2>";
}
$stmt->close();
echo '<br><a href="?">返回重新登录</a>';
} else {
// 显示登录表单
echo '<h2>用户登录系统</h2>';
echo '<p>数据库: 20232417db | 表: onetable</p>';
// 显示数据库中的用户(仅用于测试)
$result = $conn->query("SELECT name FROM onetable");
if ($result && $result->num_rows > 0) {
echo '<p>数据库中的用户: ';
$users = [];
while ($row = $result->fetch_assoc()) {
$users[] = $row['name'];
}
echo implode(', ', $users) . '</p>';
}
echo '
<form method="post" style="border:1px solid #ccc; padding:20px; width:300px;">
<div style="margin-bottom:15px;">
<label>用户名:</label><br>
<input type="text" name="username" required style="width:100%; padding:5px;">
</div>
<div style="margin-bottom:15px;">
<label>密码:</label><br>
<input type="password" name="password" required style="width:100%; padding:5px;">
</div>
<input type="submit" value="登录" style="padding:10px 20px; background:#4CAF50; color:white; border:none; cursor:pointer;">
</form>
<div style="margin-top:20px; background:#f0f0f0; padding:15px;">
<h4>测试用例:</h4>
<ul>
<li>用户: <strong>20232417pdj</strong> | 密码: <strong>20232417pdj</strong></li>
<li>用户: <strong>20232417</strong> | 密码: <strong>20232417+20251208</strong></li>
</ul>
</div>';
}
// 关闭数据库连接
$conn->close();
?>
可以看到使用正确的用户名和密码可以成功登录,而用户名或密码错误时,也可以给出相应的提示。


2.5最简单的SQL注入,XSS攻击测试
2.5.1SQL注入
因为之前的php代码对用户的输入限制,修改之后,用户名输入正确,密码输入' OR '1'='1这样就构成了一个永远为真的条件,所以不用知道密码就可以成功登录。

或者就算不知道用户名,也可以在后面加' OR '1'='1,道理一样的,也可以成功实现登录。

2.5.2XSS攻击
在用户名框中输入 <img src=x onerror=alert('XSS成功')>,实现XSS攻击,这个攻击的原理是标签尝试加载一个不存在的图片src="x",图片根本不存在,所以肯定会加载失败,那么就会触发 onerror 事件,所以onerror属性中的JavaScript代码alert('XSS成功')就会被执行,最后弹出警告框显示"XSS成功"。

2.6成SQL注入、XSS、CSRF攻击
在主机上安装webgoat,在webgoat上进行练习。
2.6.1完成SQL注入攻击
选择webgoat中的Injection
(1)第9题
这一部分需要通过SQL注入实现对表中所有数据的查询。该部分给出的语句为:"SELECT * FROM user_data WHERE first_name = 'John' AND last_name = '____ + ____+ ____';需要从给出的几个选项中选择出正确的选项,首先我们需要设置一个永真条件,另外还需要使得前面的单引号闭合,所以需要选择的三个数据是Smith';or;'1'='1

(2)第10题
这一部分的语句是:
"SELECT * FROM user_data WHERE login_count = " + Login_Count + " AND userid = " + User_ID;
那么只要使得login_count=1,userid=1 OR 1=1,即可达成永真的条件

2.6.2完成XSS攻击
这一部分要选择Injection下的Cross Site Scripting
(1)第7题
第7题是反射型XSS攻击。
输入<script>alert('20232417xss attack succeed')</script>即可成功实现攻击。

(2)第10题
这个问题需要找到测试代码的路径来触发DOM-based XSS攻击。
题目中的应用程序使用hash片段进行客户端路由,基础路由是start.mvc#lesson。在JavaScript路由配置中发现了测试路由'test/:param': 'testRoute',这意味着开发阶段留下的测试接口在生产环境中未被移除。攻击者通过构造start.mvc#test/路径就能访问到这个隐藏的测试功能,再加上测试路由很可能没有对传入的参数进行安全过滤,直接将用户输入插入到DOM中执行。所以成功实现攻击。

2.6.3完成CSRF攻击
这一部分需要选择Cross-Site Request Forgeries部分。
第8题
首先我们需要创建一个新的账号,前提条件是新用户的用户名为csrf-+原来的用户名,在我们的用户登录的同时,在一个新的页面登录这个新账号,登录成功后点击练习页面的solved,就可以成功实现攻击。

3.问题以及解决方案
- 问题1:在刚刚开始实验的时候遇到无法将复制粘贴任何进虚拟机的问题
- 解决方法:在询问同学后得知,需要重新安装VMware tools,所以参考教程重新安装了。
学习思考以及感悟
通过动手实践Web应用从搭建到被攻破的全过程,我学到了许多。当我输入<u>HTML injection succeed.</u>,网页上神奇地出现了一条带下划线的文本,这便是HTML注入最直观的演示——因为我的代码没有对用户输入进行任何过滤或转义,浏览器便将输入解析为HTML指令并执行。这警示我,任何可能被解释为代码的输入,都必须经过严格的“消毒”,防止其扰乱预期的页面结构。而更危险的JavaScript注入则更具动态破坏性。输入<img src='x' onerror='document.body.innerHTML = "JavaScript injection succeed."'>,由于图片加载失败,onerror事件被触发,整个网页内容被瞬间替换。在SQL注入环节,我输入' OR '1'='1绕过登录验证时,这句看似简单的输入让数据库执行了非预期的查询、从而让“密码错误”的提示变为“欢迎登录成功”时,我明白了将用户输入直接拼接到SQL语句中是何等危险。数据库作为应用的核心,其防线一旦被攻破,后果不堪设想。这驱使我立即学习并应用参数化查询(预编译语句),从根源上确保用户输入被当作不可执行的“数据”而非“代码”的一部分来处理。这些攻击都指向同一个核心问题:跨站脚本攻击(XSS)。无论是注入的HTML标签还是JavaScript脚本,其本质都是攻击者将恶意代码“植入”到网页中,并在其他用户的浏览器中执行。防御XSS的关键,在于对输出到页面的所有动态内容(如用户提交的评论、用户名)都进行HTML实体转义。这样,<会被转换为无害的<,script标签便不再会被浏览器识别为可执行代码。最终,在WebGoat平台上对跨站请求伪造(CSRF) 的演练,揭示了另一种维度的风险:攻击者如何利用用户已登录的信任状态,诱导其点击一个链接或访问一个页面,从而在用户不知情的情况下,以其身份执行非本意的操作(如转账、改密)。这让我认识到,关键操作不能仅依赖Cookie或Session进行身份验证,必须引入额外的、不可预测的令牌(如CSRF Token)进行校验,才能有效抵御这种“借刀杀人”式的攻击。综合来看,这次实验如同一场深刻的“攻防演练”。它让我从被动地接受安全理论,转变为主动地以攻击者思维审视每一行代码、每一个输入点。真正的安全,是一种将“永不信任,始终验证”的原则内化于开发全过程的持续实践。这绝非终点,而是我未来在构建任何数字系统时,都必须携带上路的、最重要的思维行囊。

浙公网安备 33010602011771号