简介:这份资源是华中科技大学网络空间安全学院「信息系统安全实验」的完整课程资料包,面向信息安全、网络工程等专业的学生及自学者,用于课程设计、实验复现与动手实践。压缩包共73个文件,约4.65MB,以C语言源码(17个.c、7个.h)为核心,辅以Python脚本、Shell脚本、Makefile、HTML/JS页面及说明文档,并配有26张实验截图,覆盖从源码阅读到环境搭建的完整链路。内容按lab1、lab2、lab3三个进阶阶段组织,涉及密码学、访问控制、漏洞利用、XSS与CSRF、SQL注入、chroot隔离等典型安全主题,源码可自行修改,便于在真实环境中调试与验证。目前已有133人学习下载。读者可借此获得一套结构清晰的实验体系,既能对照说明书逐步完成实验,也能通过修改源码深入理解漏洞成因与修复思路,适合作为课程作业参考或安全入门实战练习。
1. 信息系统安全实验包:从源码到说明书的完整复现路径
拿到一个「信息系统安全实验」的压缩包,里面带源码和说明书,这件事本身就值得认真对待。华中科技大学网络空间安全学院的这套实验材料,核心价值不在于它有多高深,而在于它把信息系统安全里那些散落在教材各章的知识点——身份认证、访问控制、加密传输、日志审计、漏洞利用与防御——串成了一条可以动手跑的链路。你如果正在做网络空间安全毕设,或者带课设需要一套能讲清楚「攻击怎么发生、防御怎么落地」的案例,这套东西的参考价值比网上那些只给结论不给过程的资料高出一截。它适合两类人:一类是刚接触安全实验、需要照着步骤把环境跑起来的新手;另一类是做课程设计或毕设、想拿一套完整代码做二次开发的老手。源码可改,说明书可查,这意味着你不仅能复现,还能拆开看每一处逻辑为什么这么写。
2. 实验环境搭建与源码结构拆解
2.1 先看清压缩包里有什么再动手
拿到压缩包之后,不要急着解压完就双击运行。我一般会先做一件事:在终端里列出文件树,把目录层级和文件类型摸清楚。信息系统安全实验通常包含几类东西——Web 应用源码、数据库脚本、配置文件、实验指导书(PDF 或 Word)、以及可能的抓包或日志样本。你先用命令看一眼结构,心里有数了再决定从哪个入口进。
# 列出压缩包内容,不急着解压 unzip -l 信息系统安全实验.zip # 解压到指定目录,避免污染当前工作区 mkdir -p ~/lab/iss && unzip 信息系统安全实验.zip -d ~/lab/iss # 查看目录树,重点关注源码目录和文档目录 find ~/lab/iss -maxdepth 3 -type d | sort find ~/lab/iss -maxdepth 3 -type f \( -name "*.py" -o -name "*.java" -o -name "*.php" -o -name "*.sql" -o -name "*.md" -o -name "*.pdf" \) | sort这几条命令的逻辑很直接:unzip -l先看清单,确认没有路径穿越之类的异常条目;解压时指定独立目录,方便后面清理;find按类型筛出源码和文档,让你快速判断这套实验是 Web 方向、二进制方向还是网络协议方向。参数上,-maxdepth 3是防止目录太深导致输出爆炸,实际用的时候可以根据压缩包层级调整。如果发现源码是 Java Web 项目,那大概率需要 Tomcat 或 Spring Boot 环境;如果是 Python 脚本,重点看依赖文件里有没有 requirements.txt 或 Pipfile。
2.2 依赖安装与运行环境对齐
源码能跑起来的前提是环境对得上。信息系统安全实验常见的坑是:说明书里写的 Python 版本是 3.8,你本地是 3.12,某些加密库的 API 已经变了;或者数据库脚本用的是 MySQL 5.7 的语法,你装了 MySQL 8.0,默认字符集和认证插件不一样,导入直接报错。我的习惯是先读说明书里的「实验环境」章节,把版本号抄下来,然后用虚拟环境隔离。
# Python 项目:创建虚拟环境并安装依赖 cd ~/lab/iss/src python3 -m venv venv source venv/bin/activate pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 如果依赖里有 mysqlclient 或 cryptography 编译失败,先装系统级开发包 # Ubuntu/Debian: sudo apt-get install -y python3-dev default-libmysqlclient-dev build-essential # CentOS/RHEL: sudo yum install -y python3-devel mysql-devel gcc这里的关键参数是-i指定的镜像源,国内环境下拉包会快很多。虚拟环境的作用不只是隔离,更重要的是当你把某个库升级到不兼容版本时,可以直接删掉 venv 重来,不用折腾系统 Python。如果源码是 Java 的,对应做法是检查 pom.xml 或 build.gradle 里的 JDK 版本要求,用 SDKMAN 或手动切换 JAVA_HOME。数据库方面,导入 SQL 文件之前先确认字符集:SHOW VARIABLES LIKE 'character_set%';,如果是 latin1 就改成 utf8mb4,否则中文实验数据会变乱码。
2.3 源码目录的模块划分与阅读顺序
一套信息系统安全实验的源码,通常按功能模块分目录。以常见的 Web 安全实验为例,你可能会看到auth/、crypto/、access_control/、audit/这样的结构。阅读顺序建议从入口文件开始,顺着请求处理链路往下走,而不是一上来就扎进加密算法实现里。先看app.py或index.php怎么路由,再看中间件里做了哪些安全检查,最后看具体业务逻辑里哪里存在可控输入。
# 示例:一个典型的 Flask 入口文件结构(根据实际源码调整) from flask import Flask, request, session from auth import login_required from crypto import encrypt_data, decrypt_data from access_control import check_permission app = Flask(__name__) app.secret_key = 'lab_secret_key_change_me' @app.route('/login', methods=['POST']) def login(): username = request.form.get('username') password = request.form.get('password') # 这里通常是第一个实验观察点:密码是否明文传输、是否做了哈希 if verify_user(username, password): session['user'] = username return '登录成功' return '登录失败', 401 @app.route('/data') @login_required def get_data(): # 第二个观察点:访问控制是在装饰器里做的还是散落在业务代码里 if not check_permission(session.get('user'), 'read_data'): return '无权访问', 403 return encrypt_data(fetch_sensitive_data())读这段代码的时候,重点不是它写了什么,而是它没写什么。比如app.secret_key硬编码在源码里,这就是一个典型的实验观察点——说明书里可能会让你改成从环境变量读取。verify_user的实现如果直接拼接 SQL 查询,那就是 SQL 注入的实验入口。encrypt_data用的密钥如果也是硬编码,那加密实验的意义就变成了「找出密钥管理的问题」。带着这种「找茬」的心态读源码,比被动跟着说明书走一遍收获大得多。
3. 核心安全模块的实验操作与参数调优
3.1 身份认证实验:从明文比对到哈希加盐
身份认证是信息系统安全实验里最基础也最容易出彩的模块。很多实验包一开始给的是明文密码比对,让你改成哈希存储,再加盐,再引入慢哈希算法。这个过程不是一步到位的,每一步都有具体的参数要调。我一般会按「明文 → MD5 → SHA256 → SHA256+盐 → bcrypt/argon2」这个顺序做对比实验,每改一版就跑一次登录流程,观察数据库里存的值和登录耗时。
import hashlib import os import time # 第一版:明文存储(仅用于实验对比,生产环境绝对不能用) def store_plain(password): return password # 第二版:MD5 无盐 def store_md5(password): return hashlib.md5(password.encode()).hexdigest() # 第三版:SHA256 + 随机盐 def store_sha256_salt(password): salt = os.urandom(16).hex() h = hashlib.sha256((salt + password).encode()).hexdigest() return f"{salt}${h}" # 第四版:bcrypt(需要 pip install bcrypt) import bcrypt def store_bcrypt(password): salt = bcrypt.gensalt(rounds=12) return bcrypt.hashpw(password.encode(), salt).decode() # 对比登录耗时 for name, func in [('md5', store_md5), ('sha256_salt', store_sha256_salt), ('bcrypt', store_bcrypt)]: start = time.time() for _ in range(100): func('test_password_123') elapsed = time.time() - start print(f"{name}: 100次哈希耗时 {elapsed:.4f}s")这段代码的意图是让你直观看到不同方案的性能差异。MD5 和 SHA256 快得离谱,100 次哈希可能不到 0.01 秒,这意味着攻击者用 GPU 每秒可以尝试几十亿次。bcrypt 的rounds=12是成本因子,每增加 1,计算量翻倍,12 大概是 0.2~0.3 秒一次,对正常登录来说可以接受,对暴力破解来说就是灾难。实验说明书里如果只让你改代码不让你测耗时,那这个实验就少了一半的价值。参数上,bcrypt 的 rounds 建议不低于 10,argon2 的话重点调 memory_cost 和 time_cost,具体值取决于你的实验机器性能。
3.2 访问控制实验:RBAC 模型的代码落地与越权测试
访问控制实验的核心是让你实现一套基于角色的权限模型,然后自己攻击自己——用低权限账号去访问高权限接口,看能不能成功。很多实验包给的是一个简化版的 RBAC,角色和权限的映射关系存在数据库里,每次请求查一次。这里的关键参数是权限检查的粒度:是接口级、数据级还是字段级。
# 简化的 RBAC 权限检查实现 ROLE_PERMISSIONS = { 'admin': ['read', 'write', 'delete', 'manage_users'], 'editor': ['read', 'write'], 'viewer': ['read'] } def check_permission(user_role, action): """检查角色是否有执行某操作的权限""" if user_role not in ROLE_PERMISSIONS: return False return action in ROLE_PERMISSIONS[user_role] # 越权测试用例:viewer 尝试 delete def test_horizontal_privilege_escalation(): # 模拟:用户 A 是 viewer,尝试删除用户 B 的数据 user_a_role = 'viewer' action = 'delete' result = check_permission(user_a_role, action) print(f"viewer 执行 delete: {'允许(存在越权风险)' if result else '拒绝(正常)'}") # 预期输出:拒绝 # 垂直越权测试:editor 尝试 manage_users def test_vertical_privilege_escalation(): user_role = 'editor' action = 'manage_users' result = check_permission(user_role, action) print(f"editor 执行 manage_users: {'允许(存在越权风险)' if result else '拒绝(正常)'}") # 预期输出:拒绝这段代码的逻辑说明:ROLE_PERMISSIONS字典定义了角色到权限的映射,check_permission做一次集合成员判断。实验时你要做的不仅是让这个函数返回正确结果,还要在 Web 层验证——比如用 viewer 的 session 直接请求/admin/delete_user?id=2,看后端有没有在路由层就拦住。常见翻车点是:权限检查只在前端做了,后端接口裸奔;或者权限检查在业务逻辑里做了,但异常处理时直接return True。参数上,如果你要把这个模型扩展到数据级权限,需要引入资源属主的概念,检查逻辑变成「当前用户是否是资源所有者或拥有管理权限」。
3.3 加密传输实验:对称与非对称的混合使用
信息系统安全实验里加密模块通常要求你实现一个混合加密方案:用 AES 加密数据,用 RSA 加密 AES 密钥,然后通过不安全的通道传输。这个实验的难点不在算法调用,而在密钥管理和填充模式的选择。AES 的 ECB 模式是教学反面教材,CBC 模式需要随机 IV,GCM 模式自带完整性校验但参数更多。
from Crypto.Cipher import AES, PKCS1_OAEP from Crypto.PublicKey import RSA from Crypto.Random import get_random_bytes import base64 # 生成 RSA 密钥对(实验用 2048 位,生产建议 4096) key = RSA.generate(2048) private_key = key.export_key() public_key = key.publickey().export_key() # AES-GCM 加密数据 def aes_gcm_encrypt(plaintext, key): cipher = AES.new(key, AES.MODE_GCM) ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode()) return base64.b64encode(cipher.nonce + tag + ciphertext).decode() # RSA-OAEP 加密 AES 密钥 def rsa_encrypt_key(aes_key, pub_key): rsa_cipher = PKCS1_OAEP.new(RSA.import_key(pub_key)) return base64.b64encode(rsa_cipher.encrypt(aes_key)).decode() # 完整流程 aes_key = get_random_bytes(32) # AES-256 encrypted_data = aes_gcm_encrypt("敏感实验数据", aes_key) encrypted_key = rsa_encrypt_key(aes_key, public_key) print(f"加密后数据: {encrypted_data[:50]}...") print(f"加密后密钥: {encrypted_key[:50]}...")参数说明:AES 密钥长度选 32 字节对应 AES-256,16 字节对应 AES-128,实验里建议用 256 位。GCM 模式的 nonce 是 16 字节,每次加密必须重新生成,绝对不能复用,否则会泄露密钥流。RSA 的 OAEP 填充比 PKCS1 v1.5 更安全,但密文长度会略长。实验说明书里如果让你对比 ECB 和 CBC 的密文特征,你可以加密一张纯色图片,ECB 模式下能看出轮廓,CBC 模式下看不出,这个演示效果很直观。坑在于:Python 的 pycryptodome 库和 cryptography 库 API 不一样,源码里用哪个就跟哪个,不要混用。
4. 避坑与排查:实验包里最容易翻车的五个地方
4.1 数据库导入报错「Unknown character set」
现象:执行source init.sql时提示ERROR 1115 (42000): Unknown character set: 'utf8mb4'。原因:MySQL 版本低于 5.5.3 不支持 utf8mb4,或者客户端连接时指定的字符集不对。解决:先SELECT VERSION();确认版本,低于 5.5.3 就把 SQL 文件里的 utf8mb4 全部替换成 utf8;连接时用mysql --default-character-set=utf8mb4 -u root -p。
4.2 加密库导入失败「No module named 'Crypto'」
现象:from Crypto.Cipher import AES报 ModuleNotFoundError。原因:安装的包名是pycryptodome但导入名是Crypto,或者系统里同时装了pycrypto和pycryptodome导致冲突。解决:先pip uninstall pycrypto pycryptodome -y,再pip install pycryptodome。如果还不行,检查虚拟环境是否激活,which python确认路径。
4.3 权限检查绕过「装饰器顺序写反了」
现象:加了@login_required和@check_permission两个装饰器,未登录用户居然能访问受保护接口。原因:装饰器执行顺序是从下往上的,如果@check_permission写在@login_required下面,权限检查会先执行,此时 session 里还没有用户信息,检查逻辑可能直接放行。解决:把@login_required放在最上面,确保先认证再鉴权。验证方法:用 curl 不带 cookie 请求接口,看返回 401 还是 200。
4.4 日志审计实验「日志文件权限过大」
现象:实验要求记录所有敏感操作日志,你写到了/var/log/app/audit.log,但同组同学能直接cat看到全部内容,甚至能删除。原因:日志文件权限是 644,属主是 root 但目录权限是 777。解决:chmod 640 audit.log,目录chmod 750,属组设为应用运行用户。更彻底的做法是日志实时发送到远程 syslog 服务器,本地只留只读副本。
4.5 源码修改后「说明书对不上代码」
现象:你按自己的理解改了认证逻辑,但说明书里的实验步骤还是旧流程,导致后面做访问控制实验时找不到对应的函数。原因:说明书和源码是两套独立维护的文档,修改源码后没有同步更新说明。解决:改代码前先git init做版本控制,每改一个模块就提交一次,提交信息写清楚改了哪个函数、为什么改。说明书用 Markdown 重写一份放在源码同级目录,和代码一起提交。这样即使后面忘了,也能通过git log回溯。
5. 从实验包到毕设:二次开发与验证技巧
这套实验包如果只是跑一遍,价值有限。真正能让你在毕设或课程设计里拉开差距的,是在它基础上做二次开发,并且用可验证的方式证明你的改动确实提升了安全性。我一般会从三个方向切入:第一,把硬编码的密钥和密码全部迁移到环境变量或配置中心,然后写一个检查脚本,扫描源码里是否还有password =、secret_key =这类模式;第二,给关键操作加上不可篡改的审计日志,用 HMAC 链式签名,每条日志包含前一条的哈希,这样删改任何一条都会导致链断裂;第三,把权限模型从静态 RBAC 扩展到 ABAC,引入资源属主和时间条件。
验证方法上,不要只靠「我觉得安全了」。用工具跑一遍:bandit扫 Python 源码的常见漏洞,sqlmap测 SQL 注入是否真的堵住了,curl手动构造越权请求看返回码。下面这个脚本用来验证审计日志的完整性:
import hmac import hashlib import json class AuditLog: def __init__(self, secret): self.secret = secret.encode() self.chain = [] def append(self, event): prev_hash = self.chain[-1]['hash'] if self.chain else '0' * 64 record = {'event': event, 'prev': prev_hash} msg = json.dumps(record, sort_keys=True).encode() record['hash'] = hmac.new(self.secret, msg, hashlib.sha256).hexdigest() self.chain.append(record) def verify(self): for i, record in enumerate(self.chain): prev_hash = self.chain[i-1]['hash'] if i > 0 else '0' * 64 if record['prev'] != prev_hash: return f"链断裂在第 {i} 条" msg = json.dumps({'event': record['event'], 'prev': record['prev']}, sort_keys=True).encode() expected = hmac.new(self.secret, msg, hashlib.sha256).hexdigest() if not hmac.compare_digest(record['hash'], expected): return f"哈希不匹配在第 {i} 条" return "审计链完整" # 使用示例 log = AuditLog('lab_audit_secret') log.append('用户 admin 登录') log.append('用户 admin 修改了用户 viewer 的权限') log.append('用户 admin 删除了日志文件') print(log.verify()) # 输出:审计链完整 # 模拟篡改 log.chain[1]['event'] = '用户 admin 什么都没做' print(log.verify()) # 输出:哈希不匹配在第 1 条这段代码的关键在于hmac.compare_digest做恒定时间比较,防止时序攻击;prev字段把每条日志串成链,任何一条被改都会导致后续所有哈希对不上。参数上,secret 要从环境变量读取,不要写在代码里。验证的时候,先跑正常流程确认返回「审计链完整」,再手动改一条记录看是否报错。这个技巧在毕设答辩时很加分,因为你能现场演示「篡改必被发现」。
最后说一个我自己的习惯:每次拿到新的实验包,先不改任何代码,原样跑通一遍,把每一步的输出截图或保存日志。然后再开始改,每改一处就对比改动前后的输出差异。这样即使改崩了,也能快速回退到已知可用的状态。信息系统安全这个方向,动手踩过的坑比看过的书更值钱。希望帮到你。
本文还有配套的精品资源,点击获取