拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

机器学习检测SQL注入实战:七种模型代码包解析与避坑指南

机器学习检测SQL注入实战:七种模型代码包解析与避坑指南

简介:这份资源面向网络安全初学者、机器学习入门者以及需要构建SQL注入检测方案的安全开发人员,核心目标是用多种经典机器学习算法区分SQL注入语句与正常语句,属于可动手复现的实战型项目。压缩包共36个文件,约1.06MB,包含10个csv样本数据、10个Python脚本、8个已训练模型文件,以及少量xml配置、说明文档与缓存文件;csv用于存放正常与注入样本矩阵,py脚本覆盖特征预处理与各算法训练流程,model文件则保存了训练好的分类器,便于直接加载测试。目前已有291人学习下载。读者可从中获得一套完整的SQL注入检测实验框架:featurepossess.py负责原始样本预处理与特征提取,sqlsvm.py、adaboost.py、sqlkNN.py等分别对应SVM、Adaboost、KNN、决策树、随机森林、逻辑斯蒂回归与贝叶斯等算法的训练实现,testsql.py则用准确率评估模型效果,方便横向对比不同算法在注入识别任务上的表现,并据此理解特征工程与模型选型对检测性能的影响。

1. 从一份 SQL 注入检测代码包说起:它到底能跑出什么结果

如果你手头有一批 HTTP 请求日志,想快速判断哪些参数像是 SQL 注入、哪些是正常查询,又不想从零搭特征工程,这个机器学习检测SQL注入.zip值得拆开看看。它把 SQL 注入语句和正常语句当成二分类问题,用 SVM、Adaboost、决策树、随机森林、逻辑斯蒂回归、KNN、贝叶斯等算法各训了一版模型,file目录里直接放着训练好的.model文件,data目录里是配套的矩阵样本。换句话说,拿到手就能先跑testsql.py看准确率,再决定要不要换特征或调参。适合刚接触机器学习安全检测的从业者、做课程设计的学生,以及想给 WAF 规则做补充验证的工程师。它不替代生产级检测引擎,但作为可复现的基线实验,省掉了自己攒样本和搭流程的时间。

2. 样本矩阵与特征工程:featurepossess.py到底提了什么

2.1 从原始语句到数值矩阵的转换逻辑

这个项目最核心的资产不是模型,而是data目录里那批已经转好的 CSV 矩阵。featurepossess.py负责把原始 SQL 语句或请求参数转成数值特征,输出all_matrix.csv、nor_matrix.csv、sql_matrix.csv等文件。常见做法是统计关键词出现次数、特殊字符频率、语句长度、数字与字母比例、注释符与引号配对情况等,再拼成固定长度的向量。你不需要完全照搬它的特征列表,但必须理解一点:SQL 注入检测的误报率高低,八成取决于特征是否区分了“正常业务里也会出现的引号和关键字”与“攻击载荷特有的拼接模式”。

下面这段代码模拟了featurepossess.py里最可能出现的特征提取骨架,我按常见实现补全了注释和参数说明:

import re import numpy as np # 关键词表:覆盖联合查询、报错注入、布尔盲注常见 token SQL_KEYWORDS = [ "select", "union", "insert", "update", "delete", "drop", "or", "and", "where", "from", "sleep", "benchmark", "information_schema", "concat", "group_concat", "load_file" ] # 特殊字符表:引号、注释符、分号、括号、百分号等 SPECIAL_CHARS = ["'", '"', "--", "#", ";", "(", ")", "%", "*", "="] def extract_features(payload: str) -> np.ndarray: """ 输入:单条待检测字符串(URL 参数值或 SQL 片段) 输出:一维数值向量,顺序与训练矩阵列一致 """ lower = payload.lower() feats = [] # 1. 关键词命中次数,归一化前先取对数平滑 for kw in SQL_KEYWORDS: feats.append(lower.count(kw)) # 2. 特殊字符出现次数 for ch in SPECIAL_CHARS: feats.append(lower.count(ch)) # 3. 统计型特征:长度、数字占比、字母占比、空格数 length = len(payload) if len(payload) > 0 else 1 digit_ratio = sum(c.isdigit() for c in payload) / length alpha_ratio = sum(c.isalpha() for c in payload) / length space_count = payload.count(" ") feats.extend([length, digit_ratio, alpha_ratio, space_count]) # 4. 正则特征:是否出现典型注入片段 feats.append(1 if re.search(r"union\s+select", lower) else 0) feats.append(1 if re.search(r"or\s+\d+\s*=\s*\d+", lower) else 0) feats.append(1 if re.search(r"sleep\s*\(\s*\d+\s*\)", lower) else 0) return np.array(feats, dtype=float)

逻辑说明:前两段是词频统计,第三段是整体统计量,第四段是正则命中标记。参数上,SQL_KEYWORDS和SPECIAL_CHARS的长度决定了最终特征维度,必须和训练时保持一致,否则模型加载后会报维度不匹配。length做了非零保护,避免空字符串导致除零。如果你要替换成自己的样本,改完特征函数后必须重新生成所有*_matrix.csv,不能只改测试集。

2.2 数据文件命名与用途对照

data目录里文件不少,初次打开容易懵。我按实际训练流程把它们分成三组:

文件名用途是否参与训练
all_matrix.csv全量样本矩阵,通常含标签列是,用于最终训练
nor_matrix.csv正常语句矩阵,标签为 0是,与 sql_matrix 合并
sql_matrix.csv注入语句矩阵,标签为 1是,与 nor_matrix 合并
all_matrix.txt全量矩阵的文本备份,便于排查否,仅参考
normal_test.csv正常语句测试集否,仅测试
sql_test.csv注入语句测试集否,仅测试
alltest_matrix.csv合并后的测试矩阵否,仅测试
nortest_matrix.csv正常测试矩阵的另一种命名否,仅测试
sqltest_matrix.csv注入测试矩阵的另一种命名否,仅测试
sqlnew.csv新增注入样本,可能是补充数据视情况,需确认标签
normal_less.csv正常样本的欠采样版本视情况,用于平衡

提示:sqlnew.csv和normal_less.csv没有在 README 里明确说明用途,我一般会先看列数和标签分布,确认它们是否已经包含在all_matrix.csv里,避免重复采样导致测试集泄漏。

如果你要自己扩充样本,建议保持nor_matrix.csv和sql_matrix.csv的列顺序完全一致,合并时用pd.concat([nor, sql], axis=0),不要用append逐行加,否则列对齐容易出玄学问题。

3. 七种模型训练脚本拆解:从sqlsvm.py到sqltree.py

3.1 各脚本对应的算法与调用方式

项目里每个算法一个脚本,命名很直白:sqlsvm.py对应 SVM,sqlkNN.py对应 KNN,sqltree.py对应决策树,sqlbys.py对应贝叶斯,sqllogistic.py对应逻辑斯蒂回归,adaboost.py对应 Adaboost,forestrandom.py对应随机森林。GBDT.model也在file目录里,说明还有一版梯度提升树,但训练脚本可能合并在其他文件里或未单独提供。这些脚本的通用流程是:读data下的矩阵,切分训练测试集,调用 sklearn 对应分类器,训练后保存模型到file目录。

下面以sqlsvm.py为例,写出典型实现,并标注参数含义:

import pandas as pd from sklearn.model_selection import train_test_split from sklearn.svm import SVC from sklearn.metrics import accuracy_score, classification_report import joblib # 读取全量矩阵,假设最后一列是标签 data = pd.read_csv("data/all_matrix.csv", header=None) X = data.iloc[:, :-1].values y = data.iloc[:, -1].values # 固定随机种子,保证每次切分一致,方便对比不同模型 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y ) # SVM 核函数选 rbf,C 控制惩罚力度,gamma 控制核宽度 clf = SVC(kernel="rbf", C=1.0, gamma="scale", probability=True) clf.fit(X_train, y_train) # 预测并输出准确率 y_pred = clf.predict(X_test) print("SVM Accuracy:", accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred)) # 保存模型,文件名与 file 目录下已有模型对应 joblib.dump(clf, "file/svm.model")

逻辑说明:train_test_split里stratify=y很重要,SQL 注入样本通常少于正常样本,不分层会导致测试集里正负比例失衡,准确率虚高。SVC的probability=True会启用概率估计,训练变慢但方便后续做阈值调整。joblib.dump保存的模型可以直接被testsql.py加载。参数上,C越大越容易过拟合,gamma越大决策边界越复杂,这两个值是 SVM 在注入检测里最常翻车的地方。

其他脚本结构类似,只是分类器换掉。adaboost.py通常用AdaBoostClassifier,基分类器默认是决策树深度 1;forestrandom.py用RandomForestClassifier,n_estimators一般设 100 到 300;sqlkNN.py用KNeighborsClassifier,n_neighbors默认 5,但注入检测里样本不平衡时 KNN 容易偏向多数类,需要配合距离权重。

3.2 模型文件格式与加载验证

file目录下的.model文件是序列化后的 sklearn 对象,常见格式是 joblib 或 pickle。testsql.py负责加载这些模型并输出准确率。如果你换了 Python 或 sklearn 版本,加载旧模型可能报InconsistentVersionWarning甚至直接失败。我一般会先跑一遍加载验证:

import joblib import os model_dir = "file" for fname in os.listdir(model_dir): if fname.endswith(".model"): path = os.path.join(model_dir, fname) try: model = joblib.load(path) print(f"{fname} loaded, type={type(model).__name__}") except Exception as e: print(f"{fname} failed: {e}")

这段代码不训练,只验证模型能否反序列化。如果某个模型加载失败,优先检查 sklearn 版本,而不是重新训练。常见做法是建一个虚拟环境,把 sklearn 版本固定到训练时的版本,再跑testsql.py。

注意:不要用pickle.load直接读来源不明的.model文件,joblib 对 numpy 数组的兼容性更好,也更安全。

4. 测试脚本testsql.py与准确率复现:怎么跑、看什么指标

4.1 测试流程与输出解读

testsql.py是整个项目里最值得先跑的文件。它加载file目录下的模型,读取data下的测试矩阵,输出每个模型的准确率。典型实现如下:

import pandas as pd import joblib from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 读取测试矩阵,最后一列为真实标签 test_data = pd.read_csv("data/alltest_matrix.csv", header=None) X_test = test_data.iloc[:, :-1].values y_test = test_data.iloc[:, -1].values models = { "SVM": "file/svm.model", "KNN": "file/knn.model", "Tree": "file/tree.model", "RandomForest": "file/forestrandom.model", "Logistic": "file/lg.model", "Adaboost": "file/Adaboost.model", "Bayes": "file/bys.model", "GBDT": "file/GBDT.model", } for name, path in models.items(): model = joblib.load(path) y_pred = model.predict(X_test) acc = accuracy_score(y_test, y_pred) prec = precision_score(y_test, y_pred, zero_division=0) rec = recall_score(y_test, y_pred, zero_division=0) f1 = f1_score(y_test, y_pred, zero_division=0) print(f"{name}: acc={acc:.4f}, precision={prec:.4f}, recall={rec:.4f}, f1={f1:.4f}")

逻辑说明:准确率只反映整体正确比例,在样本不平衡时参考价值有限。我建议同时看 precision 和 recall。SQL 注入检测里,recall 低意味着漏报多,precision 低意味着误报多。zero_division=0避免某类样本为零时报警告。参数上,alltest_matrix.csv的列数必须和训练矩阵一致,否则predict会直接抛维度错误。

4.2 复现时最容易对不上的三个地方

第一,测试矩阵的列顺序。如果你自己重新生成了特征,列顺序和训练时不一致,模型不会报错但准确率会崩。第二,标签编码。有的脚本用 0/1,有的用 -1/1,加载模型前必须确认测试标签的编码方式和训练一致。第三,随机种子。train_test_split的random_state不同,切分结果不同,准确率会有几个百分点的波动,这是正常现象,不要误以为模型坏了。

提示:跑testsql.py之前,先用pandas.read_csv打印测试矩阵的shape和标签分布,确认正负样本比例和训练集接近。

5. 避坑与排查:模型加载失败、准确率虚高、特征维度不匹配

5.1 模型加载报错ModuleNotFoundError或版本警告

现象:joblib.load时抛出ModuleNotFoundError: No module named 'sklearn.svm._classes'或InconsistentVersionWarning。原因:训练模型的 sklearn 版本和当前环境不一致,旧版本序列化的类路径在新版本里变了。解决:建虚拟环境,安装训练时使用的 sklearn 版本,常见是 0.19 到 0.22 之间的版本。如果不知道具体版本,先试pip install scikit-learn==0.22.2,再跑加载验证。

5.2 准确率异常高,接近 100%

现象:testsql.py输出某个模型准确率 0.99 以上。原因:测试集和训练集有重叠,或者特征里包含了标签泄漏信息。all_matrix.csv和alltest_matrix.csv如果来自同一次采样且没有去重,模型只是记住了样本。解决:检查两个文件的交集,用pd.merge找重复行;确认特征提取时没有把标签列误当成特征。常见做法是重新切分数据,确保测试集完全独立。

5.3 特征维度不匹配ValueError: X has n features, but model is expecting m

现象:加载模型后predict报维度错误。原因:featurepossess.py改动后重新生成了矩阵,但模型还是旧版本。解决:要么用新矩阵重新训练所有模型,要么回退特征函数到训练时的版本。不要试图用reshape硬凑维度,那样预测结果没有意义。

5.4 某个模型准确率明显低于其他

现象:KNN 或贝叶斯准确率只有 0.6 左右,其他模型 0.9 以上。原因:KNN 对特征尺度敏感,如果特征里有的列数值范围是 0 到 1,有的是 0 到 1000,距离计算会被大数值主导。贝叶斯假设特征独立,而 SQL 关键词之间高度相关。解决:对 KNN 做归一化,用StandardScaler或MinMaxScaler;贝叶斯可以换用ComplementNB或先做特征选择。这不是代码 bug,是算法假设和数据分布不匹配。

5.5sqlnew.csv和normal_less.csv标签不明

现象:想用这两个文件扩充训练集,但不知道标签列是哪一列。原因:README 没有说明,文件命名也不统一。解决:先看列数和all_matrix.csv是否一致,再用value_counts看最后一列的分布。如果最后一列只有 0 和 1,大概率是标签;如果全是连续值,可能是特征矩阵,需要结合其他文件判断。不确定的情况下,不要直接合并进训练集。

6. 进阶用法:用featurepossess.py接自己的日志,做一轮真实流量验证

把项目跑通只是第一步,真正有价值的是用它验证你自己的流量。我一般会从 Nginx 或应用日志里抽一批带参数的请求,先人工标 200 条左右,正常和注入各半,然后走一遍featurepossess.py的特征提取,生成自己的测试矩阵。这里有个细节:训练时的特征函数和测试时必须完全一致,所以不要直接改featurepossess.py的原函数,而是复制一份,改完后重新训练所有模型,再用testsql.py的框架跑对比。

具体操作上,先把日志里的参数值解析成字符串列表,逐条调用特征函数,拼成矩阵后保存为my_test_matrix.csv。然后用下面的代码批量跑所有模型,输出对比表:

import pandas as pd import joblib import numpy as np from featurepossess import extract_features # 假设你已复制并调整好 # 从日志解析出的参数列表 payloads = ["1' or '1'='1", "admin", "select * from users", "normal_search_keyword"] labels = [1, 0, 1, 0] # 人工标注,1 为注入,0 为正常 X = np.array([extract_features(p) for p in payloads]) y = np.array(labels) results = [] for name, path in { "SVM": "file/svm.model", "RandomForest": "file/forestrandom.model", "Adaboost": "file/Adaboost.model", "GBDT": "file/GBDT.model", }.items(): model = joblib.load(path) pred = model.predict(X) acc = (pred == y).mean() results.append({"model": name, "accuracy": acc, "pred": pred.tolist()}) df = pd.DataFrame(results) print(df)

这段代码的关键参数是payloads和labels,它们决定了验证集的质量。200 条里如果正常样本占 90%,准确率再高也不能说明模型能抓注入。我习惯按 1:1 或 1:2 配比,并且把正常样本里包含引号、关键字但确实不是攻击的 case 单独挑出来,这些才是误报的主要来源。

跑完一轮后,重点看两个指标:漏报的注入样本里,有多少是因为特征里没有覆盖对应的注入模式;误报的正常样本里,有多少是因为关键词命中次数过高。前者需要补特征,后者需要调阈值或换模型。随机森林和 GBDT 通常在这类任务里表现更稳,SVM 对小样本更友好,但特征一多就慢。Adaboost 对噪声敏感,如果标注里有错标,它的准确率会明显掉。

从那以后我每次拿到新的检测脚本,都强制先跑一遍加载验证和标签分布检查,再谈调参。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表