
简介本资源是一套面向网络安全与人工智能交叉领域学习者的Web攻击检测实践项目聚焦XSS与SQL注入两类主流Web漏洞的智能识别与防御适用于具备Python基础及机器学习入门知识的安全开发、渗透测试或AI安全方向学习者。压缩包共66个文件含17个核心Python源码含数据预处理、特征工程、聚类与监督模型训练模块、3个pkl/h5模型文件、5张可视化结果图、3个CSV样本数据集及多份README说明文档整体26.58MB结构清晰覆盖从数据采集、特征提取到模型部署的完整流程。已有300人学习下载资源包含两个并行WAF实现其一基于K-means等无监督聚类方法实现异常流量初筛其二采用SVM、随机森林等有监督算法构建高精度分类器辅以Word2Vec文本向量化与PCAP网络流量解析能力提供可复现、可调优的端到端检测方案。1. 这不是传统规则引擎WAF它用聚类发现未知XSS变种靠分类器拦截SQL注入绕过载荷你见过能自动识别“img/srcx/onerroralert(1)”这种非标准XSS语法的WAF吗传统正则匹配型WAF会漏掉它——因为没写在规则库里。而这个基于机器学习的web攻击检测系统把HTTP请求体当作文本向量用K-Means聚类在无标签数据中揪出异常行为簇再用随机森林分类器对SQL注入载荷做细粒度判别。它不依赖人工维护的规则库而是从真实流量中学习“正常请求”的分布边界。适合安全团队做二次开发、红队评估WAF绕过能力、或作为轻量级API网关的AI层补充。项目结构清晰分两套实现AiWaf-1聚类驱动专注XSS异常检测AiWaf-2监督学习驱动主攻SQL注入分类二者共享同一套特征工程模块和预处理流水线。如果你手头有Nginx access log或Burp Suite导出的HTTP历史请求就能直接喂给它训练——不需要标注每条请求是攻击还是正常。2. 特征工程决定检测上限从原始HTTP请求到可训练向量的三步转换2.1 为什么不能直接用原始URL或POST body训练原始HTTP请求包含大量噪声时间戳、IP、User-Agent、Cookie等与攻击无关字段同时存在编码差异如%3Cscript%3Evsscript、空格变形select/**/from、大小写混用SeLeCt。若直接将整条请求字符串哈希或TF-IDF向量化模型会把“不同UA访问同一页面”误判为异常或因编码不一致导致相同攻击载荷被映射到不同向量空间。因此必须先做语义归一化——剥离协议层干扰聚焦payload核心结构。提示项目中code/preprocess.py的normalize_request()函数是关键入口它不调用任何第三方解码库而是用内置urllib.parse.unquote()正则清洗组合确保所有%xx编码统一还原且保留script、union select等语义标记符的原始形态。2.1.1 请求解析与字段提取系统默认解析Apache/Nginx标准access log格式%h %l %u %t \%r\ %s %b但更推荐使用code/parse_http.py处理原始HTTP报文。该模块支持两种输入模式Log模式从access log提取request_uri和request_body字段Raw模式接收完整HTTP报文含headers用\r\n\r\n分割header/body# 示例解析单条HTTP请求 from code.parse_http import parse_http_request raw_http bGET /search?qscriptalert(1)/script HTTP/1.1\r\nHost: example.com\r\n\r\n parsed parse_http_request(raw_http) print(parsed[uri]) # /search?qscriptalert(1)/script print(parsed[body]) # print(parsed[method]) # GET逻辑说明parse_http_request()先定位\r\n\r\n位置再用正则提取GET|POST方法、/path?queryURI、以及body内容。对于POST请求它会检查Content-Length头并截取对应字节数避免读取不完整body。2.1.2 关键字段标准化处理表字段类型处理方式参数说明为何必须URI Query参数urllib.parse.parse_qs()解析后对每个value做unquote()strip()encodingutf-8强制指定编码防止GBK乱码导致向量错位避免q%u4F60%E5%A5%BDUTF-8编码中文被当作乱码丢弃POST Body按application/x-www-form-urlencoded或text/plain解析忽略multipart/form-data需额外解析器keep_blank_valuesTrue保留空值参数因id可能为SQL注入点空参数常被用于绕过if not param:校验HTTP Method转为大写后映射为数字GET→0, POST→1, PUT→2, DELETE→3使用固定映射而非one-hot节省向量维度方法本身携带强语义无需高维稀疏表示User-Agent截取前32字符MD5哈希生成8位十六进制字符串hashlib.md5(ua.encode()).hexdigest()[:8]防止UA字符串过长拖慢TF-IDF计算且哈希后仍保留设备指纹特性2.2 特征向量化TF-IDF n-gram组合为何比BERT更适配WAF场景项目未采用BERT等大模型原因很实际WAF需毫秒级响应而BERT-base单次推理在CPU上需200ms。本系统选用sklearn.feature_extraction.text.TfidfVectorizer配置ngram_range(1,3)即同时提取单词、双词组合、三词组合。例如scriptalert(1)/script会被拆解为unigramscript,alert,(1),/scriptbigramscriptalert,alert(1),(1)/scripttrigramscriptalert(1),alert(1)/script# code/feature_engineer.py 中的核心配置 from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer( max_features10000, # 限制词典大小防止内存爆炸 ngram_range(1, 3), # 捕获局部语法结构如 union select from stop_wordsNone, # 不设停用词因 script 是关键攻击标记 lowercaseFalse, # 保留大小写因 UNION SELECT 和 union select 在DBMS中敏感度不同 token_patternr[\w\\\(\)\{\}\[\]\;\:\.\,\!\?\-\_\/], # 自定义token正则保留HTML/SQL符号 )参数说明token_pattern是关键——它允许,,(,)等符号作为token一部分使img srcx onerroralert(1)被切分为img,srcx,onerroralert(1)而非按空格粗暴分割。若用默认\w整个onerroralert(1)会被当做一个token丢失内部结构。2.2.1 特征向量维度压缩策略10000维TF-IDF向量直接输入模型会导致过拟合尤其当训练样本仅千级别时。项目采用TruncatedSVD进行降维from sklearn.decomposition import TruncatedSVD svd TruncatedSVD(n_components256, random_state42) # 降至256维 X_reduced svd.fit_transform(X_tfidf) # X_tfidf shape: (n_samples, 10000)逻辑说明TruncatedSVD比PCA更适合稀疏TF-IDF矩阵它通过奇异值分解保留最大256个方向的能量实测在AiWaf-2的SQL注入检测任务中F1-score仅下降0.8%但推理速度提升3.2倍。降维后向量可直接送入随机森林或SVM避免深度学习模型带来的部署复杂度。2.3 标签体系设计聚类无监督 vs 分类有监督的双轨标注逻辑AiWaf-1聚类版完全不依赖人工标签其检测逻辑是对正常流量如爬虫日志、用户访问日志提取TF-IDF向量用K-Meansk5聚类得到5个“正常行为簇”中心新请求向量到各簇中心的欧氏距离 阈值δ则判定为异常AiWaf-2分类版需构造正负样本正样本攻击OWASP ZAP导出的XSS/SQLi测试用例data/xss_payloads.txt,data/sqli_payloads.txt负样本正常从生产环境access log随机采样过滤含script、union select等关键词的请求注意项目未提供自动标注脚本需手动执行code/generate_labels.py它会扫描data/raw_logs/目录下所有log文件用正则匹配script.*?|javascript:|onerror|alert\(|UNION\sSELECT|AND\s11等模式打标。正则表达式已优化避免误报如UNION SELECT要求前后有空格或括号包围排除UNION_SELECT_TABLE这类变量名。3. 双模型架构实战聚类检测XSS异常与分类拦截SQL注入的代码级实现3.1 AiWaf-1基于K-Means的XSS异常检测流水线AiWaf-1的核心思想是——XSS载荷在向量空间中必然偏离正常请求分布。它不关心“这是哪种XSS”只判断“这像不像正常人发的请求”。整个流程分三阶段离线建模、在线预测、阈值自适应。3.1.1 离线建模用正常流量训练K-Means模型# 进入AiWaf-1目录准备正常流量数据 cd AiWaf-1 mkdir -p data/normal_logs # 假设已有10万条正常access log存于 /var/log/nginx/access.log head -n 100000 /var/log/nginx/access.log data/normal_logs/train.log # 执行建模自动完成解析→向量化→聚类 python code/train_kmeans.py \ --log_path data/normal_logs/train.log \ --model_save_path model/kmeans_xss.joblib \ --n_clusters 5 \ --distance_threshold 12.5命令说明--log_path指定正常流量日志路径支持gzip压缩文件自动解压--model_save_path保存聚类模型及向量化器后续预测需加载同一套transformer--n_clusters 5经验性设为5对应“首页访问”、“搜索请求”、“API调用”、“图片加载”、“静态资源”五类行为--distance_threshold 12.5欧氏距离阈值初始值由code/analyze_distance.py在训练集上统计95%分位数得出逻辑说明train_kmeans.py先调用preprocess.py解析log再用feature_engineer.py生成TF-IDF向量最后用sklearn.cluster.KMeans训练。关键点在于——它保存的不仅是KMeans模型还包括TfidfVectorizer和TruncatedSVD对象确保预测时特征空间严格一致。3.1.2 在线预测单请求毫秒级异常判定# code/predict_kmeans.py from sklearn.cluster import KMeans import joblib import numpy as np class XSSDetector: def __init__(self, model_path): self.model joblib.load(model_path) # 加载含vectorizersvdkmeans的完整pipeline self.threshold self.model[threshold] # 从模型中读取预设阈值 def predict(self, http_request): # 步骤1解析请求 → 获取uribody parsed parse_http_request(http_request) text parsed[uri] parsed[body] # 步骤2向量化复用训练时的vectorizer X_vec self.model[vectorizer].fit_transform([text]) X_red self.model[svd].transform(X_vec) # 步骤3计算到各簇中心的最小距离 distances np.array([ np.linalg.norm(X_red - center) for center in self.model[kmeans].cluster_centers_ ]) min_dist distances.min() return min_dist self.threshold, float(min_dist) detector XSSDetector(model/kmeans_xss.joblib) is_attack, distance detector.predict(bGET /search?qsvg/onloadalert(1) HTTP/1.1\r\n\r\n) print(fAttack: {is_attack}, Distance: {distance:.2f}) # Attack: True, Distance: 15.32参数说明min_dist越小表示越接近某个正常行为簇 threshold才报警。阈值12.5并非固定值code/adaptive_threshold.py支持根据最近1000次预测结果动态调整——若连续50次距离8.0则阈值下调0.3避免漏报若连续20次距离18.0则上调0.5减少误报。3.2 AiWaf-2基于随机森林的SQL注入分类器部署AiWaf-2采用监督学习目标是区分“id1 union select 1,2,3 from users”和“id1”。它比聚类版更精准但需标注数据。3.2.1 数据准备与特征生成项目自带data/sqli_payloads.txt237条经典SQLi载荷和data/normal_queries.txt1500条真实搜索词需先合并并生成标签# 合并正负样本添加标签列1攻击0正常 awk {print $0 \t1} data/sqli_payloads.txt data/labeled_data.tsv awk {print $0 \t0} data/normal_queries.txt data/labeled_data.tsv # 执行特征生成输出为libsvm格式兼容多种模型 python code/prepare_svm_data.py \ --input data/labeled_data.tsv \ --output data/svm_features.txt \ --max_features 5000prepare_svm_data.py会对每行文本如id1 union select 1,2,3 from users调用preprocess.normalize_query()清洗用TfidfVectorizer生成5000维向量输出libsvm格式1 1:0.234 2:0.112 ...第一列为label后续为feature_id:value3.2.2 训练与验证随机森林为何优于XGBoost# code/train_rf.py from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split import numpy as np # 加载libsvm格式数据 X, y load_svmlight_file(data/svm_features.txt) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 训练RF非XGBoost因RF对小数据更鲁棒且无需GPU rf RandomForestClassifier( n_estimators200, # 树数量200在精度/速度间平衡 max_depth12, # 限制树深度防过拟合 min_samples_split5, # 节点分裂最小样本数避免噪声主导 class_weightbalanced, # 自动调整正负样本权重因攻击样本少 n_jobs-1 # 利用所有CPU核心 ) rf.fit(X_train, y_train) # 验证结果 y_pred rf.predict(X_test) print(classification_report(y_test, y_pred)) # precision recall f1-score support # 0 0.98 0.99 0.98 298 # 1 0.95 0.92 0.93 52逻辑说明class_weightbalanced是关键——当攻击样本仅占3.4%52/1550时它会自动给正样本赋予约29倍权重使模型不再“懒惰地全判为正常”。max_depth12经网格搜索确定更深的树15在测试集上F1仅提升0.2%但训练时间翻倍。3.2.3 模型集成如何用单个API端点同时服务聚类与分类code/app.py提供Flask接口根据请求头X-WAF-Mode选择引擎app.route(/detect, methods[POST]) def detect_attack(): http_raw request.get_data() mode request.headers.get(X-WAF-Mode, hybrid) # default to hybrid if mode kmeans: is_attack, dist kmeans_detector.predict(http_raw) return jsonify({attack: bool(is_attack), distance: dist}) elif mode rf: is_attack rf_classifier.predict_single(http_raw) # 封装了parse→vectorize→predict return jsonify({attack: bool(is_attack)}) else: # hybrid mode: both engines vote k_res, k_dist kmeans_detector.predict(http_raw) r_res rf_classifier.predict_single(http_raw) final k_res or r_res # 只要一个判攻击即拦截 return jsonify({ attack: bool(final), kmeans_result: bool(k_res), rf_result: bool(r_res) })提示生产环境建议用hybrid模式。实测显示K-Means对新型XSS如img/srcx/onerrorfetch(//attacker.com/?cdocument.cookie)检出率高RF对SQLi绕过如id1/*comment*/union/*comment*/select更准二者互补降低漏报。4. 模型验证与绕过对抗用OWASP Benchmark测试集量化检测能力4.1 构建标准化测试集从OWASP Benchmark提取有效载荷OWASP Benchmark是权威WAF测试框架但其XML格式需转换。项目提供code/convert_benchmark.py提取HTTP请求# 下载OWASP Benchmark v1.2需手动从官网获取benchmark-1.2.zip unzip benchmark-1.2.zip python code/convert_benchmark.py \ --xml_path benchmark/benchmark-1.2.xml \ --output_dir data/benchmark_requests/ \ --attack_types xss,sqli该脚本解析XML提取test-case中的request节点保存为纯文本HTTP请求文件如xss_001.txt。最终生成data/benchmark_requests/xss/127个XSS测试用例data/benchmark_requests/sqli/98个SQLi测试用例data/benchmark_requests/normal/200个正常请求从Benchmark的safe标签提取4.1.1 测试结果对比表聚类 vs 分类 vs 混合模式测试集K-Means (AiWaf-1)Random Forest (AiWaf-2)Hybrid ModeXSS 检出率89.2% (113/127)76.4% (97/127)94.5% (120/127)SQLi 检出率61.2% (60/98)92.9% (91/98)93.9% (92/98)正常请求误报率2.5% (5/200)1.0% (2/200)1.5% (3/200)平均响应延迟8.3ms12.7ms15.2ms数据说明Hybrid模式虽延迟略高但XSS检出率提升5.3个百分点——这源于K-Means捕获了RF未见过的XSS变种如svgscripteval(atob(YWxlcnQoMSk))/script/svg。误报率控制在2%内符合生产WAF要求5%。4.2 主动对抗测试模拟常见WAF绕过手法验证鲁棒性仅用Benchmark不够需手动构造绕过载荷验证模型泛化能力。项目data/evade_tests/目录包含12种绕过技术实测样本绕过类型示例载荷AiWaf-1 结果AiWaf-2 结果关键修复点编码混淆%3Cscript%3Ealert(1)%3C%2Fscript%3E✅ 检出unquote后还原✅ 检出preprocess.py强制解码大小写混合UnIoN SeLeCt 1,2,3 FrOm users❌ 漏报✅ 检出RF的TF-IDF保留大小写bigram捕获UnIoN SeLeCt注释干扰id1/*!UNION*//*!SELECT*/1,2,3✅ 检出正则清洗注释✅ 检出normalize_query()移除/*!...*/和--字符串拼接id1 and (select substr(username,1,1) from users)a❌ 漏报✅ 检出RF训练数据含类似载荷ngram捕获substrfrom组合提示针对大小写混合漏报可在AiWaf-1的TfidfVectorizer中添加lowercaseTrue但会降低对UNION SELECTDBMS关键字需大写的敏感度。权衡后项目选择保持lowercaseFalse依赖AiWaf-2补位。4.3 模型更新机制如何让WAF持续学习新攻击生产环境中攻击手法不断进化模型需定期更新。项目提供增量训练脚本# 收集线上拦截日志假设存于 /var/log/waf/block.log tail -n 1000 /var/log/waf/block.log data/online_blocks.log # 提取被拦截请求人工确认是否真为攻击存入 data/confirmed_attacks.txt # 然后执行增量训练 python code/incremental_train.py \ --new_attacks data/confirmed_attacks.txt \ --model_path model/rf_sqli.joblib \ --output_path model/rf_sqli_v2.joblibincremental_train.py逻辑加载原RF模型的feature_importances_识别top-100重要特征如union,select,from,information_schema将新攻击样本向量化仅用这100维特征重新训练子模型用sklearn.ensemble.VotingClassifier融合新旧模型投票决策此机制避免全量重训100条新样本可在30秒内完成更新且F1-score提升1.2%。5. 生产部署技巧Docker一键启动、Nginx反向代理集成与性能调优5.1 Docker Compose部署隔离环境秒级启停项目根目录提供docker-compose.yml一键启动Flask API与Redis缓存用于存储误报反馈version: 3.8 services: waf-api: build: . ports: - 5000:5000 environment: - PYTHONUNBUFFERED1 - MODEhybrid depends_on: - redis volumes: - ./model:/app/model - ./data:/app/data redis: image: redis:7-alpine command: redis-server --save 60 1 --loglevel warning ports: - 6379:6379构建镜像时Dockerfile已预装依赖FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [gunicorn, --bind, 0.0.0.0:5000, --workers, 4, code.app:app]注意gunicorn替代Flask内置server--workers 4适配4核CPU实测QPS从120提升至890。若部署在8核机器可改为--workers 8。5.2 Nginx反向代理集成将WAF嵌入现有Web架构在Nginx配置中插入WAF检测层/etc/nginx/conf.d/waf.confupstream waf_backend { server 127.0.0.1:5000; } server { listen 8080; location / { # 步骤1记录原始请求体避免多次读取 set $req_body ; if ($request_method POST) { set $req_body $request_body; } # 步骤2转发请求到WAF API proxy_pass http://waf_backend/detect; proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Original-Method $request_method; proxy_set_header X-Original-Body $req_body; proxy_set_header X-WAF-Mode hybrid; # 步骤3根据WAF响应决定是否放行 proxy_intercept_errors on; error_page 403 block; } location block { return 403 Blocked by AI-WAF; } }逻辑说明Nginx在proxy_pass前捕获$request_body通过X-Original-Body头透传给WAF。WAF返回{attack:true}时Nginx触发error_page 403拦截。此方案无需修改后端应用代码兼容所有语言栈。5.3 性能调优CPU绑定、向量化加速与内存映射在高并发场景下需进一步优化5.3.1 CPU亲和性绑定Linux# 启动时绑定到CPU核心2,3避免上下文切换 taskset -c 2,3 gunicorn --bind 0.0.0.0:5000 --workers 2 code.app:app5.3.2 向量化加速用NumPy替代Python循环code/feature_engineer.py中向量距离计算原为Python循环# 优化前慢 distances [] for center in centers: dist 0 for i in range(len(vec)): dist (vec[i] - center[i])**2 distances.append(dist**0.5) # 优化后快3.8倍 distances np.sqrt(np.sum((vec - centers)**2, axis1))5.3.3 内存映射模型加载大模型不阻塞主线程model/kmeans_xss.joblib达120MB直接joblib.load()会阻塞Flask启动。改用numpy.memmap# model_loader.py import numpy as np from sklearn.externals import joblib def load_model_mmap(model_path): # 将joblib文件转为memmap格式首次运行需执行一次 if not os.path.exists(model_path .mmap): model joblib.load(model_path) # 仅序列化核心数组centers, vectorizer.vocabulary_等 np.save(model_path .mmap, { centers: model[kmeans].cluster_centers_, vocabulary: list(model[vectorizer].vocabulary_.keys()), idf: model[vectorizer].idf_, }) # 加载时仅mmap核心数组不加载整个joblib对象 mmap_data np.load(model_path .mmap, mmap_moder) return mmap_data[centers], mmap_data[vocabulary]实测启动时间从3.2秒降至0.7秒内存占用减少65%。本文还有配套的精品资源点击获取