简介:本资源是一套面向计算机专业本科生的高分毕业设计项目,聚焦基于监督学习的Web入侵检测系统实现,适用于毕业设计、期末大作业及课程设计场景,尤其适合具备Python基础但缺乏安全项目经验的学习者。压缩包共60个文件,含18个Jupyter Notebook(含数据预处理、特征工程、模型训练与评估全流程代码)、14个HTML/TEXT格式的样本日志与攻击载荷数据、8个核心Python脚本(如reptile.py、core.py、wordProcess.py等),以及模型参数文件(pkl/pickle)和README说明文档,整体仅2.25MB,轻量易部署。已有145人学习下载,项目为作者手打完成,获导师高度认可并取得98分高分,代码全程注释详尽,关键步骤(如payload提取、字符向量化、正常流量参数建模)均有独立notebook演示,配套数据清洗、重复去除、参数保存等实用工具脚本,结构清晰、模块解耦,开箱即用。
1. 为什么毕业设计选「监督学习 Web 入侵检测」不是跟风,而是踩中了工程落地的甜点区?
很多同学做毕业设计时看到“Web安全”“AI检测”就热血上头,直接冲向无监督异常检测、图神经网络建模HTTP流、甚至自己搭流量镜像平台——结果卡在数据采集不合法、特征工程黑盒、模型上线后误报率飙到87%。而真正能按时答辩、代码可运行、文档能过审、答辩老师点头说“有工程意识”的方案,反而是这个看似“老派”的方向:基于监督学习的 Web 入侵检测系统。它不追求SOTA指标,但直击三个硬需求:第一,训练数据明确(SQLi/XSS/PathTraversal等标签清晰);第二,特征可解释(URL长度、参数个数、特殊符号频次等全是白盒);第三,部署极轻量(单个Flask服务+Scikit-learn模型,50MB内存跑满)。我带过的某高校毕业设计小组里,A同学用这个路径两周跑通端到端流程,后续三个月全花在调参、写文档、补测试用例上,而不是和Kubernetes权限、流量抓包权限、模型蒸馏失败死磕。如果你的毕设目标是“可交付、可演示、可复现”,而不是“发论文、冲顶会”,那这个标题不是退而求其次,而是精准卡位。
2. 从原始日志到结构化样本:Web攻击数据怎么采、怎么标、怎么防污染
2.1 为什么不用公开数据集?真实日志才是你的毕业设计护城河
公开数据集如CIC-IDS2017、NSL-KDD存在严重水土不服:它们模拟的是网络层攻击(SYN Flood、Port Scan),而Web入侵本质是应用层语义攻击(?id=1' OR '1'='1)。更关键的是,这些数据集没有你服务器真实的Nginx/Apache日志格式、WAF拦截规则、业务参数命名习惯。毕业设计最忌讳“用别人的数据,解自己的题”。正确做法是:用本地Docker起一个带漏洞的靶场(如DVWA或WebGoat),再配一个轻量代理(Burp Suite Community或mitmproxy)记录所有请求-响应对。重点不是“多”,而是“准”——只录你主动构造的10类攻击载荷(含变种),每类20条,再混入30条正常业务请求(登录、搜索、分页)。这样最终得到约500条高质量样本,比下载2GB的CIC数据却不会清洗强十倍。
2.2 日志解析脚本:三行正则搞定Apache/Nginx通用字段提取
import re import pandas as pd # Apache日志示例: 127.0.0.1 - - [10/Jan/2024:14:22:01 +0000] "GET /dvwa/vulnerabilities/sqli/?id=1%27%20OR%20%271%27%3D%271&Submit=Submit HTTP/1.1" 200 3212 # Nginx日志示例: 127.0.0.1 - - [10/Jan/2024:14:22:01 +0000] "GET /dvwa/vulnerabilities/sqli/?id=1%27%20OR%20%271%27%3D%271&Submit=Submit HTTP/1.1" 200 3212 "-" "Mozilla/5.0" def parse_log_line(line): # 提取IP、时间、请求方法、URL、状态码 pattern = r'(\d+\.\d+\.\d+\.\d+) - - \[(.*?)\] "(GET|POST|PUT|DELETE) (.*?) HTTP/.*?" (\d+)' match = re.search(pattern, line) if not match: return None ip, time, method, url, status = match.groups() return { 'ip': ip, 'time': time, 'method': method, 'url': url, 'status': int(status), 'label': 'normal' # 后续按攻击特征覆盖 } # 批量处理日志文件 logs = [] with open('access.log', 'r') as f: for line in f: parsed = parse_log_line(line.strip()) if parsed: logs.append(parsed) df = pd.DataFrame(logs)提示:这个脚本不依赖第三方日志解析库(如logparser),避免答辩时被问“为什么不用XX库”。正则已适配Apache和Nginx默认日志格式,若你用自定义格式,只需改
pattern字符串——这是答辩时展示“我懂日志结构”的关键证据。
2.3 攻击标签注入:用规则引擎打标,拒绝人工标注翻车
人工给500条URL打“SQLi/XSS/Normal”标签?耗时且主观。正确姿势是:用正则规则自动打标,既快又可复现。例如:
| 攻击类型 | 正则模式(URL中匹配) | 示例 |
|---|---|---|
| SQLi | `(?i)(union\s+select | sleep( |
| XSS | `(?i)(<script | javascript: |
| Path Traversal | `(?i)(../ | ..|/etc/passwd)` |
import re attack_rules = { 'sqli': r"(?i)(union\s+select|sleep\(|benchmark\(|'\s*or\s*'1'\s*=\s*'1)", 'xss': r"(?i)(<script|javascript:|onerror=|src=data:text/html)", 'path_traversal': r"(?i)(\.\./|\.\.\\|/etc/passwd)" } def auto_label_url(url): for attack_type, pattern in attack_rules.items(): if re.search(pattern, url): return attack_type return 'normal' df['label'] = df['url'].apply(auto_label_url) print(df['label'].value_counts()) # 立刻验证标签分布参数说明:
(?i)开启忽略大小写,避免漏掉UNION SELECT;r""原生字符串防止转义错误;re.search而非re.match确保匹配URL任意位置。这步做完,你的数据集就自带“可审计性”——答辩老师问“怎么标的数据?”,你直接贴这10行代码。
3. 特征工程不是玄学:6个必选Web语义特征,每个都能解释给导师听
3.1 URL结构特征:为什么长度、参数个数比TF-IDF更稳?
深度学习常把URL喂进BERT,但毕业设计要的是“可解释、易调试、低资源”。我们用6个手工特征,全部来自URL解析后的结构信息:
from urllib.parse import urlparse, parse_qs def extract_url_features(url): parsed = urlparse(url) query_params = parse_qs(parsed.query) return { 'url_length': len(url), 'path_depth': len(parsed.path.strip('/').split('/')), 'param_count': len(query_params), 'param_avg_len': sum(len(v[0]) for v in query_params.values()) / max(len(query_params), 1), 'special_char_ratio': len(re.findall(r'[\'\"\<\>\(\)\{\}\[\]]', url)) / max(len(url), 1), 'encoded_char_ratio': len(re.findall(r'%[0-9A-Fa-f]{2}', url)) / max(len(url), 1) } # 应用到DataFrame feature_df = pd.DataFrame([extract_url_features(u) for u in df['url']]) X = pd.concat([df[['method', 'status']], feature_df], axis=1) y = df['label']逻辑说明:
path_depth反映路由复杂度(/api/v1/user/123比/login更深,更可能被绕过);param_avg_len长参数常含编码payload(%3Cscript%3E);special_char_ratio直接统计危险符号密度。这些特征不依赖词典,不需预训练,答辩时指着图表说“XSS样本的special_char_ratio均值是0.12,正常请求是0.02”,老师立刻懂你在做什么。
3.2 方法与状态码组合:被90%初学者忽略的强判别特征
单纯看URL会漏掉关键信号。比如POST /login返回200是正常,但返回500且响应体含mysql_fetch_array,就是典型SQLi泄露。所以必须加入:
method_status_combo: 将method和status拼成字符串(如"POST_500")is_error_status:status >= 400
X['method_status_combo'] = X['method'] + '_' + X['status'].astype(str) X['is_error_status'] = (X['status'] >= 400).astype(int) # 对method_status_combo做One-Hot编码(仅保留出现频次>2的组合) combo_counts = X['method_status_combo'].value_counts() valid_combos = combo_counts[combo_counts > 2].index X_combo = pd.get_dummies(X['method_status_combo'], prefix='combo') X_combo = X_combo.reindex(columns=valid_combos, fill_value=0) # 对齐列 X = pd.concat([X.drop(['method_status_combo', 'method', 'status'], axis=1), X_combo], axis=1)为什么有效:
GET_200在正常流量中高频,但在SQLi中极少(攻击者通常用GET_500触发报错);POST_302常见于登录成功跳转,但POST_200返回长HTML可能是XSS反射点。这个特征让模型学会“看上下文”,不是只盯URL。
4. 模型选型与训练:为什么Random Forest比XGBoost更适合毕业设计
4.1 不是模型越新越好:毕业设计的三个硬约束
- 可复现性:XGBoost需要调
n_estimators、max_depth、learning_rate、subsample等8个参数,而Random Forest调2个就够; - 可解释性:
sklearn的RandomForestClassifier自带feature_importances_,画个柱状图就能讲清“URL长度最重要”; - 鲁棒性:当你的样本只有500条时,XGBoost容易过拟合(验证集AUC 0.98,测试集跌到0.72),而RF靠bagging天然抗噪。
4.2 最小可行训练脚本:12行代码跑通,附关键参数注释
from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report, confusion_matrix import numpy as np # 划分数据集(毕业设计不搞K折,用固定比例更透明) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 初始化模型:n_estimators=100是经验值,max_depth=10防过拟合,class_weight='balanced'解决样本不均衡 rf = RandomForestClassifier( n_estimators=100, max_depth=10, class_weight='balanced', # 当sqli样本只有20条,normal有300条时,此参数强制模型关注少数类 random_state=42, n_jobs=-1 # 用满CPU核心,训练快 ) rf.fit(X_train, y_train) y_pred = rf.predict(X_test) print(classification_report(y_test, y_pred))参数说明:
class_weight='balanced'不是可选项,是必选项——你的攻击样本必然远少于正常请求,不加这个,模型会直接预测“全是normal”,准确率95%但毫无意义;max_depth=10是血泪经验:设为None时,树深度常超30,特征重要性图变成毛线团,答辩时无法解释。
4.3 特征重要性可视化:答辩PPT里唯一需要放的图
import matplotlib.pyplot as plt # 获取特征名(注意:One-Hot后的列名很长,取前20个) feature_names = X.columns.tolist() importance = rf.feature_importances_ indices = np.argsort(importance)[::-1][:10] # 取Top10 plt.figure(figsize=(10, 6)) plt.title("Top 10 Feature Importances") plt.bar(range(len(indices)), importance[indices]) plt.xticks(range(len(indices)), [feature_names[i] for i in indices], rotation=45) plt.tight_layout() plt.savefig('feature_importance.png', dpi=300) plt.show()答辩话术:“这张图显示URL长度和special_char_ratio贡献最大,印证了我们的假设:攻击载荷往往比正常请求更长、包含更多危险符号。而method_status_combo中的‘GET_500’组合排第4,说明报错响应是SQLi的关键线索。”
5. 避坑指南:毕业设计答辩前必须扫清的5个致命雷区
5.1 现象:模型在训练集上准确率99%,测试集只有65%
原因:未做特征缩放,且混淆了训练/测试数据划分逻辑。Random Forest虽不严格要求归一化,但当url_length(值域0-2000)和is_error_status(0/1)共存时,树分裂会偏向大数值特征;更致命的是,你可能用train_test_split前没重置索引,导致iloc[:80]切片取到同一会话的连续请求,造成数据泄露。
解决:删除所有StandardScaler相关代码(RF不需要),改用train_test_split(..., shuffle=True, stratify=y),并打印X_train.index.min(), X_train.index.max()确认索引无序。
5.2 现象:predict()返回'sqli',但输入URL是/login?user=admin
原因:标签注入规则太宽泛。比如正则r"(?i)admin"会把所有含admin的URL标为攻击,而/admin/dashboard是合法后台。
解决:重写规则,限定匹配位置。将SQLi规则改为r"(?i)(?<=\?|&)(id|user|pass)=.*?(union\s+select|sleep\()",即只在查询参数值中匹配,且要求前面是=号。
5.3 现象:Flask服务启动报错ValueError: Input contains NaN
原因:URL解析时遇到空值(如/api?无参数),parse_qs返回{},导致param_avg_len计算除零,产生inf或NaN。
解决:在extract_url_features函数开头加守卫:
if not url or '?' not in url: return {k: 0.0 for k in ['url_length', 'path_depth', 'param_count', 'param_avg_len', 'special_char_ratio', 'encoded_char_ratio']}5.4 现象:答辩演示时,输入/sqli?id=1' AND 1=1--被判定为normal
原因:URL编码未解码。日志里记录的是/sqli?id=1%27%20AND%201%3D1--,但你的正则在原始字符串上匹配,%27不等于'。
解决:在auto_label_url和特征提取前,统一解码:
from urllib.parse import unquote url_decoded = unquote(url) # 后续所有操作基于url_decoded5.5 现象:feature_importances_全为0,或某特征重要性突兀地高达0.9
原因:One-Hot编码后列名含非法字符(如combo_GET_500中的-),sklearn内部处理出错;或X中混入非数值列(如method未编码)。
解决:检查X.dtypes,确保全为float64或int64;用X.columns = X.columns.str.replace('[^a-zA-Z0-9_]', '_')清洗列名;删除所有含object类型的列。
6. 毕业设计加分项:用Flask封装API,3步实现“可演示、可截图、可录屏”的终端效果
6.1 构建最小API服务:不依赖数据库,纯内存加载模型
from flask import Flask, request, jsonify import joblib import pandas as pd from urllib.parse import unquote app = Flask(__name__) # 加载训练好的模型和特征提取函数(保存在train.py中) model = joblib.load('rf_model.pkl') # 用joblib.dump(rf, 'rf_model.pkl')保存 # 注意:extract_url_features和auto_label_url函数需复制到app.py或单独模块 @app.route('/detect', methods=['POST']) def detect_attack(): data = request.get_json() url = data.get('url', '') if not url: return jsonify({'error': 'URL required'}), 400 # 解码并提取特征 url_decoded = unquote(url) features = extract_url_features(url_decoded) # 转为DataFrame(必须和训练时列顺序一致) X_input = pd.DataFrame([features]) # 补齐One-Hot列(用0填充缺失列) for col in model.feature_names_in_: if col not in X_input.columns: X_input[col] = 0 X_input = X_input[model.feature_names_in_] # 严格对齐 pred = model.predict(X_input)[0] proba = model.predict_proba(X_input)[0].max() return jsonify({ 'prediction': pred, 'confidence': float(proba), 'details': 'High confidence' if proba > 0.8 else 'Medium confidence' }) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False) # 关闭debug,避免生产环境风险为什么用Flask不用FastAPI:毕业设计答辩环境常禁用异步(老师电脑没装asyncio)、禁用Pydantic(额外依赖),Flask零配置、
pip install flask一步到位,curl -X POST http://localhost:5000/detect -H "Content-Type: application/json" -d '{"url":"/sqli?id=1\' OR \'1\'=\'1"}'命令一行就能演示,截图直接放PPT。
6.2 演示脚本:3个curl命令覆盖全部攻击类型,答辩时手不抖
# 1. SQLi演示(高置信度) curl -X POST http://localhost:5000/detect \ -H "Content-Type: application/json" \ -d '{"url":"/dvwa/vulnerabilities/sqli/?id=1%27%20OR%20%271%27%3D%271&Submit=Submit"}' # 2. XSS演示(中置信度,因部分符号被编码) curl -X POST http://localhost:5000/detect \ -H "Content-Type: application/json" \ -d '{"url":"/vulnerabilities/xss_r/?name=%3Cscript%3Ealert%281%29%3C%2Fscript%3E"}' # 3. 正常请求(验证无误报) curl -X POST http://localhost:5000/detect \ -H "Content-Type: application/json" \ -d '{"url":"/login?username=admin&password=123"}'技巧:把这三个命令写进
demo.sh,答辩时bash demo.sh | tee demo_output.txt,实时输出+存档,避免手敲出错。输出示例:{"prediction": "sqli", "confidence": 0.92, "details": "High confidence"} {"prediction": "xss", "confidence": 0.78, "details": "Medium confidence"} {"prediction": "normal", "confidence": 0.99, "details": "High confidence"}
6.3 文档撰写心法:用“问题-方案-证据”三角结构写每一章
毕业设计文档最容易被毙的是“描述性写作”。比如写“我用了Random Forest”,不如写:
问题:XGBoost在小样本下过拟合严重,验证集AUC波动达±0.15,无法稳定输出结果。
方案:改用Random Forest,设置max_depth=10限制树复杂度,class_weight='balanced'缓解样本不均衡。
证据:测试集F1-score从0.62提升至0.84,且5次重复实验标准差仅0.02(见表3-2)。
这种写法让导师一眼看到你的工程决策链。我带的A同学按此结构写文档,盲审直接获评“优秀”,理由是“问题意识清晰,验证过程扎实”。
最后说句实在话:毕业设计不是科研,是工程能力的快照。当你能把500条日志解析清楚、6个特征解释明白、5个坑踩过一遍、3个curl命令演示流畅,你就已经超越了80%的同学。那些炫技的Transformer、GNN、实时流处理,留到入职后再卷。现在,先把监督学习的Web入侵检测跑通、写透、讲明白——这本身就是一种稀缺能力。希望帮到你。
本文还有配套的精品资源,点击获取