文章目录
- 一、概述
-
- [1、什么是 SQL 注入](#1、什么是 SQL 注入)
- [2、SQL 注入的危害](#2、SQL 注入的危害)
- [二、LOW 级别 ------ 无过滤的 SQL 拼接](#二、LOW 级别 —— 无过滤的 SQL 拼接)
-
- 1、漏洞描述
- 2、查看网页源代码
- 3、分析网页源代码
- 4、操作步骤
-
- (1)正常查询
- (2)判断注入类型
- (3)获取字段数量
- (4)获取当前数据库和版本
- (5)获取所有数据库名
- (6)获取当前数据库的所有表名
- [(7)获取 users 表的所有列名](#(7)获取 users 表的所有列名)
- (8)提取用户名和密码
- [(9)使用 group_concat() 一次性提取所有数据](#(9)使用 group_concat() 一次性提取所有数据)
- [5、AI视角下的 SQL 注入检测](#5、AI视角下的 SQL 注入检测)
-
- [(1)基于语义的 SQL 注入检测](#(1)基于语义的 SQL 注入检测)
- [(2)AI 辅助的 WAF 增强](#(2)AI 辅助的 WAF 增强)
- 6、LOW级别------小结
- [三、MEDIUM 级别 ------ 转义函数与下拉菜单](#三、MEDIUM 级别 —— 转义函数与下拉菜单)
-
- 1、漏洞描述
- 2、查看网页源代码
- 3、分析网页源代码
- 4、操作步骤
-
- [(1)抓包修改 id 参数](#(1)抓包修改 id 参数)
- [(2)数字型注入 Payload](#(2)数字型注入 Payload)
- [5、AI视角下的 SQL 注入检测](#5、AI视角下的 SQL 注入检测)
- 6、MEDIUM级别------小结
- [四、HIGH 级别 ------ LIMIT 限制与 SESSION 会话](#四、HIGH 级别 —— LIMIT 限制与 SESSION 会话)
-
- 1、漏洞描述
- 2、查看网页源代码
- 3、分析网页源代码
- 4、操作步骤
-
- [(1)使用注释符绕过 LIMIT 1](#(1)使用注释符绕过 LIMIT 1)
- [(2)其他 HIGH 级别 Payload](#(2)其他 HIGH 级别 Payload)
- [5、AI视角下的 SQL 注入检测](#5、AI视角下的 SQL 注入检测)
-
- [(1)LIMIT 绕过的智能检测](#(1)LIMIT 绕过的智能检测)
- (2)基于查询结果的异常检测
- 6、HIGH级别------小结
- [五、Impossible 级别 ------ 参数化查询与 CSRF Token](#五、Impossible 级别 —— 参数化查询与 CSRF Token)
-
- 1、漏洞描述
- 2、查看网页源代码
- 3、分析网页源代码
- 4、操作步骤
-
- (1)正常查询(数字输入)
- [(2)尝试 SQL 注入(被拒绝)](#(2)尝试 SQL 注入(被拒绝))
- (3)尝试非数字输入(被拒绝)
- 安全机制总结
- 5、Impossible级别------小结
- [6、AI视角下的 SQL 注入防御](#6、AI视角下的 SQL 注入防御)
-
- [(1)智能 SQL 注入检测](#(1)智能 SQL 注入检测)
- (2)自适应查询保护
- [六、LOW vs MEDIUM vs HIGH vs Impossible:防御演进对比](#六、LOW vs MEDIUM vs HIGH vs Impossible:防御演进对比)
-
- 1、防护策略演进对比
- 2、攻击面变化分析
- [3、SQL 注入防御的核心原则](#3、SQL 注入防御的核心原则)
- 七、总结
- 八、AI增强防御建议
-
- [1、智能 SQL 注入检测](#1、智能 SQL 注入检测)
- 2、基于查询结构的异常检测
- 3、实施优先级建议
- 4、总结
- 个人主页 :蒲公英eric
- 专栏传送门 :《DVWA通关全记录:从漏洞复现到安全防御》
- 学习方向:Web安全 / AI安全交叉领域
- 人生格言:安全没有终点,只有不断迭代的防御
📌 本文是专栏「DVWA通关全记录:从漏洞复现到安全防御」的第 07 篇文章。
一、概述
1、什么是 SQL 注入
SQL 注入(SQL Injection)是一种常见的 Web 安全漏洞。攻击者通过在用户输入中插入恶意的 SQL 代码,破坏原有 SQL 查询的结构,从而欺骗数据库执行非预期的操作。
SQL 注入的发生通常需要满足以下三个必要条件:
- 用户输入可控:应用程序接收来自用户的输入数据,并将其直接拼接到 SQL 语句中。
- 输入未经处理:用户输入未经过有效的过滤、转义或参数化处理,便直接用于构建 SQL 查询。
- 执行结果可回显:数据库执行查询后的结果(如错误信息、查询数据)能够被返回到前端页面,为攻击者提供了信息反馈的渠道。
2、SQL 注入的危害
SQL 注入漏洞一旦被利用,可能造成极其严重的后果,主要包括:
- 绕过身份验证:攻击者可以构造特殊输入,绕过登录验证,直接获取管理员或其他高权限账户的访问权限。
- 数据泄露与篡改:攻击者可以读取、修改甚至删除数据库中的敏感数据,如用户信息、交易记录等。
- 权限提升与横向渗透:通过获取数据库的用户名、密码等凭据,攻击者可能进一步渗透到数据库服务器或关联的其他系统。
- 服务器沦陷:在特定条件下,攻击者可能利用数据库功能在服务器上执行系统命令,或上传 WebShell,从而完全控制服务器。
二、LOW 级别 ------ 无过滤的 SQL 拼接
1、漏洞描述
LOW 级别未对用户输入进行任何检查或过滤,直接将用户提交的 id 参数拼接到 SQL 查询语句中执行,从而形成了典型的 SQL 注入漏洞。
其核心问题主要体现在以下几个方面:
- 缺乏输入验证与过滤:未对用户输入的数据进行任何合法性校验或危险字符过滤。
- 直接拼接 SQL 语句:将用户输入直接拼接到 SQL 查询字符串中,使得攻击者可以插入恶意代码。
- 可利用 UNION 查询提取数据 :攻击者能够通过构造
UNION SELECT语句,从数据库中提取任意表、任意字段的敏感信息。 - 可通过 ORDER BY 探测字段数量 :攻击者可以利用
ORDER BY子句逐步试探查询结果集的列数,为后续的 UNION 注入攻击做准备。
2、查看网页源代码
php
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
break;
case SQLITE:
global $sqlite_db_connection;
#$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']);
#$sqlite_db_connection->enableExceptions(true);
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
#print $query;
try {
$results = $sqlite_db_connection->query($query);
} catch (Exception $e) {
echo 'Caught exception: ' . $e->getMessage();
exit();
}
if ($results) {
while ($row = $results->fetchArray()) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
} else {
echo "Error in fetch ".$sqlite_db->lastErrorMsg();
}
break;
}
}
?>
3、分析网页源代码
(1)代码概述
LOW 级别的后端逻辑非常简单,主要包含以下几个步骤:
- 获取用户输入 :从
$_GET或$_REQUEST中获取用户提交的id参数。 - 直接拼接 SQL :未对
$id参数进行任何过滤、转义或验证,直接将其拼接到 SQL 查询字符串中。 - 执行查询并输出:执行拼接后的 SQL 语句,并将查询结果输出到页面。
- 错误信息回显 :如果 SQL 语句执行出错,会通过
mysqli_error()将数据库错误信息直接输出到前端。
(2)漏洞分析
① 漏洞一 ------ 完全无输入过滤
问题分析 :$id 参数未经过任何验证或过滤,直接拼接到 SQL 查询语句中。攻击者可以构造恶意的 id 值(例如 ' OR '1'='1),从而改变 SQL 语句的原始逻辑,实现注入攻击。
② 漏洞二 ------ 错误信息直接回显
问题分析 :当 SQL 语句执行出错时,mysqli_error() 返回的错误信息会直接输出到页面。这为攻击者提供了宝贵的信息泄露渠道,攻击者可以利用这些错误信息来推断数据库的表结构、字段名等敏感信息,辅助后续的注入攻击。
③ 漏洞三 ------ 字符型注入
问题分析 :查询语句 user_id = '$id' 使用单引号将变量包裹,这属于典型的字符型 SQL 注入。攻击者需要先闭合前面的单引号,才能插入并执行自己的恶意 SQL 代码。
4、操作步骤
(1)正常查询
- 登录 DVWA :使用默认凭据(例如
admin/password)登录 DVWA 后台。 - 设置安全级别 :在左侧菜单栏的 "DVWA Security" 页面中,将安全级别设置为 Low。
- 进入 SQL 注入页面:点击左侧菜单的 "SQL Injection" 进入 SQL 注入测试页面。
- 执行查询 :在输入框中输入
1,然后点击 Submit 按钮,观察页面返回的用户信息。

(2)判断注入类型
(2)判断注入类型
-
构造第一个测试 Payload :在输入框中输入
1' AND '1'='1,然后点击 Submit 按钮。- 预期结果 :页面正常返回 ID 为 1 的用户信息。这是因为
AND '1'='1'条件恒为真,查询逻辑未受影响。
- 预期结果 :页面正常返回 ID 为 1 的用户信息。这是因为
-
构造第二个测试 Payload :在输入框中输入
1' AND '1'='2,然后点击 Submit 按钮。- 预期结果 :页面无数据返回或显示错误信息。这是因为
AND '1'='2'条件恒为假,导致查询结果为空。
- 预期结果 :页面无数据返回或显示错误信息。这是因为
-
结果分析 :对比两次测试结果。如果第一次有数据返回而第二次没有,则说明应用程序对用户输入未做有效过滤,存在 SQL 注入漏洞。这种基于布尔逻辑的响应差异是判断注入点存在的典型方法。


(3)获取字段数量
-
构造探测 Payload :在输入框中依次输入以下 Payload,并点击 Submit 按钮。
1' ORDER BY 1 #1' ORDER BY 2 #1' ORDER BY 3 #
-
观察页面响应:
- 如果
ORDER BY n正常返回数据,说明查询结果集至少有n个字段。 - 如果
ORDER BY n导致页面报错、无数据返回或显示异常,则说明查询结果集的字段数小于n。
- 如果
-
结果分析:
- 预期结果 :
ORDER BY 2正常返回用户信息,而ORDER BY 3报错或无数据。 - 结论 :这表明原始查询语句返回的结果集包含 2 个字段 。此信息是后续构造
UNION SELECT攻击的关键前提。

- 预期结果 :


(4)获取当前数据库和版本
-
构造 UNION 查询 Payload :在输入框中输入以下 Payload,并点击 Submit 按钮。
1' UNION SELECT database(), version() #
-
观察页面响应:
- 页面应正常返回数据,并在原本显示用户姓名的位置,分别显示当前数据库的名称和数据库的版本信息。
-
结果分析:
- 预期结果 :页面会显示类似
ID: 1,First name: dvwa,Surname: x.x.x的信息(具体版本号会因环境而异)。 - 结论 :这表明
UNION SELECT注入成功。我们利用之前探测到的字段数(2个),成功将database()和version()函数的查询结果合并到原始查询结果中并回显,从而获取了关键的数据库环境信息。

- 预期结果 :页面会显示类似
(5)获取所有数据库名
-
构造 UNION 查询 Payload :在输入框中输入以下 Payload,并点击 Submit 按钮。
1' UNION SELECT 1, CONVERT(schema_name USING latin1) FROM information_schema.schemata #
-
观察页面响应:
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
1和当前 MySQL 实例中所有数据库的名称(每行一个数据库名)。
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
-
结果分析:
- 预期结果 :页面会显示类似
ID: 1,First name: 1,Surname: information_schema、Surname: dvwa、Surname: mysql等行(具体数据库列表会因环境而异)。 - 结论 :这表明我们成功利用
UNION SELECT查询了information_schema.schemata系统表,获取了服务器上所有数据库的列表。这是信息收集阶段的关键一步,为后续选择目标数据库进行深入渗透奠定了基础。
- 预期结果 :页面会显示类似

(6)获取当前数据库的所有表名
-
构造 UNION 查询 Payload :在输入框中输入以下 Payload,并点击 Submit 按钮。
1' UNION SELECT 1, CONVERT(table_name USING latin1) FROM information_schema.tables WHERE table_schema = database() #
-
观察页面响应:
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
1和当前数据库(dvwa)中所有表的名称(每行一个表名)。
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
-
结果分析:
- 预期结果 :页面会显示类似
ID: 1,First name: 1,Surname: guestbook、Surname: users等行(具体表列表会因环境而异)。 - 结论 :这表明我们成功利用
UNION SELECT查询了information_schema.tables系统表,并筛选出当前数据库(database()函数返回dvwa)的所有表。这是信息收集的进一步深入,为后续提取特定表(如users表)的字段和数据做好了准备。

- 预期结果 :页面会显示类似
(7)获取 users 表的所有列名
-
构造 UNION 查询 Payload :在输入框中输入以下 Payload,并点击 Submit 按钮。
1' UNION SELECT 1, CONVERT(column_name USING latin1) FROM information_schema.columns WHERE table_name = 'users' #
-
观察页面响应:
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
1和users表中的所有列名(每行一个列名)。
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
-
结果分析:
- 预期结果 :页面会显示类似
ID: 1,First name: 1,Surname: user_id、Surname: first_name、Surname: last_name、Surname: user、Surname: password、Surname: avatar等行(具体列名列表会因环境而异)。 - 结论 :这表明我们成功利用
UNION SELECT查询了information_schema.columns系统表,并筛选出users表的所有列。这是信息收集的最后一步,至此我们已经掌握了目标表(users)的完整结构,为后续提取具体数据(如用户名和密码)做好了准备。

- 预期结果 :页面会显示类似
(8)提取用户名和密码
-
构造 UNION 查询 Payload :在输入框中输入以下 Payload,并点击 Submit 按钮。
1' UNION SELECT user, password FROM users #
-
观察页面响应:
- 页面应正常返回数据,并在原本显示用户姓名的位置,分别显示
users表中所有用户的用户名和经过 MD5 加密的密码(每行一个用户)。
- 页面应正常返回数据,并在原本显示用户姓名的位置,分别显示
-
结果分析:
- 预期结果 :页面会显示类似
ID: 1,First name: admin,Surname: 5f4dcc3b5aa765d61d8327deb882cf99等行(具体用户名和密码哈希值会因环境而异)。 - 结论 :这表明我们成功利用
UNION SELECT直接查询了users表的user和password字段,获取了所有用户的登录凭据。至此,我们完成了从漏洞发现、信息收集到最终数据窃取的完整 SQL 注入攻击链。这充分证明了 LOW 级别下无过滤 SQL 拼接漏洞的严重危害性。

- 预期结果 :页面会显示类似
(9)使用 group_concat() 一次性提取所有数据
-
构造 UNION 查询 Payload :在输入框中输入以下 Payload,并点击 Submit 按钮。
1' UNION SELECT 1, group_concat(user_id, ':', user, ':', password) FROM users #
-
观察页面响应:
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
1和一个由所有用户数据拼接而成的长字符串。该字符串的格式为user_id:user:password,不同用户的数据之间默认由逗号分隔。
- 页面应正常返回数据,并在原本显示用户姓名的位置,显示一个数字
-
结果分析:
- 预期结果 :页面会显示类似
ID: 1,First name: 1,Surname: 1:admin:5f4dcc3b5aa765d61d8327deb882cf99,2:gordonb:e99a18c428cb38d5f260853678922e03,3:1337:8d3533d75ae2c3966d7e0d4fcc69216b,4:pablo:0d107d09f5bbe40cade3de5c71e9e9b7,5:smithy:5f4dcc3b5aa765d61d8327deb882cf99的信息(具体数据会因环境而异)。 - 结论 :这表明我们成功利用 MySQL 的
group_concat()函数,将users表中所有用户的user_id、user和password字段值一次性拼接并提取出来。这种方法比逐行查询更高效,能在一个请求中获取所有目标数据,进一步展示了 SQL 注入漏洞在数据窃取方面的强大能力。
- 预期结果 :页面会显示类似

5、AI视角下的 SQL 注入检测
(1)基于语义的 SQL 注入检测
传统的 SQL 注入检测主要依赖正则表达式和关键字黑名单,这种方法容易产生误报和漏报。AI 技术可以辅助构建更智能的语义检测模型,通过分析用户输入的深层语义和结构特征来识别潜在的注入攻击。
核心检测维度对比:
| 特征维度 | 正常输入 | SQL 注入攻击 |
|---|---|---|
| 输入结构 | 简单数字或字符串 | 包含 SQL 关键字(如 UNION、SELECT、DROP 等) |
| 特殊字符 | 少量或无 | 包含 '、#、--、/* */ 等 SQL 注释符或分隔符 |
| 语法模式 | 不符合 SQL 语法 | 符合或部分符合 SQL 语法结构 |
| 输入长度 | 通常较短 | 可能明显增长,包含复杂的拼接逻辑 |
技术实现方案:
- 语法分析:使用 SQL 语法解析器对用户输入进行分析,判断其是否构成有效的 SQL 语句片段。即使输入经过混淆或编码,解析器也能识别其潜在的 SQL 结构。
- 意图识别:利用自然语言处理(NLP)模型分析输入的真实意图。正常查询意图(如"搜索商品")与恶意注入意图(如"提取数据库名")在语义特征上存在显著差异。
- 异常检测:基于历史正常请求数据建立基线模型,识别偏离正常输入模式的异常请求。AI 模型可以学习正常用户的行为模式,从而更精准地发现异常。
(2)AI 辅助的 WAF 增强
传统的 Web 应用防火墙(WAF)规则更新滞后,难以应对新型或变种的注入攻击。AI 可以从以下方面增强 WAF 的防御能力:
- 动态规则生成:通过分析攻击日志和流量数据,AI 能够自动识别新的注入模式并生成相应的防护规则,实现 WAF 规则的自动化更新和迭代。
- 混淆检测:攻击者常对注入 Payload 进行编码(如 URL 编码、Base64)、注释分割等混淆处理以绕过检测。AI 模型可以学习这些混淆手法,有效识别经过处理的恶意输入。
- 上下文感知:结合请求的上下文信息(如 User-Agent、Referer、请求频率、访问路径等)进行综合风险评估。单一的输入检测可能被绕过,但结合多维度的上下文分析能显著提升判断准确性。
6、LOW级别------小结
LOW 级别的 SQL 注入漏洞演示了最基础的攻击场景:应用程序未对用户输入进行任何安全防护,攻击者可以轻易地利用 SQL 注入获取数据库中的任意敏感数据。
核心安全问题总结:
- 无任何输入验证或过滤 :用户提交的
id参数未经任何合法性校验或危险字符过滤,直接拼接到 SQL 查询语句中,为攻击者提供了直接的注入入口。 - 错误信息直接回显:当 SQL 语句执行出错时,数据库的错误信息会直接输出到前端页面。这为攻击者提供了宝贵的信息泄露渠道,使其能够推断数据库的表结构、字段名等敏感信息,辅助后续的精准攻击。
- 可直接通过 UNION 查询提取数据 :由于查询结果会回显到页面,攻击者能够利用
UNION SELECT语句将任意查询结果合并到原始结果中,从而系统地提取数据库名、表名、列名乃至具体的用户数据(如用户名和密码)。
安全启示:
LOW 级别的案例清晰地展示了"无防护即最大风险"的安全原则。在实际开发中,必须对用户输入进行严格的验证、过滤或使用参数化查询等安全编码实践,从根本上杜绝 SQL 注入漏洞的产生。同时,应避免将详细的数据库错误信息直接暴露给用户,以防止信息泄露。
三、MEDIUM 级别 ------ 转义函数与下拉菜单
1、漏洞描述
MEDIUM 级别在 LOW 级别的基础上,引入了两种看似有效的防护措施:后端使用 mysqli_real_escape_string() 函数对用户输入进行转义,前端使用下拉选择框(<select>)限制用户只能选择预设的 id 值,并且表单提交方式从 GET 改为 POST。
核心防护机制:
- 后端转义 :使用
mysqli_real_escape_string()函数对用户提交的id参数中的特殊字符(如单引号')进行转义,旨在防止其破坏 SQL 语句结构。 - 前端限制:将输入框替换为下拉菜单,用户只能从预设的选项(如 1, 2, 3, 4, 5)中选择,意图从源头限制恶意输入。
- 提交方式:表单使用 POST 方法提交,使得攻击参数不会直接暴露在 URL 中,增加了手动探测的难度。
核心安全问题:
尽管引入了上述防护,MEDIUM 级别仍然存在可被绕过的安全漏洞:
- 前端限制可被绕过 :前端下拉菜单仅是一种用户体验限制,并非安全屏障。攻击者可以轻松使用 Burp Suite、Postman 等工具拦截并修改 HTTP 请求,将
id参数替换为任意值,从而完全绕过前端限制。 - 转义函数对数字型注入无效 :
mysqli_real_escape_string()函数主要用于转义字符串中的特殊字符(如单引号、反斜杠)。然而,如果应用程序的 SQL 查询逻辑是数字型(例如WHERE user_id = $id,变量未被引号包裹),那么转义函数将不起作用,因为数字本身不需要引号闭合,攻击者可以直接注入数字型 Payload(如1 OR 1=1)进行攻击。
2、查看网页源代码
php
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Display values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
#print $query;
try {
$results = $sqlite_db_connection->query($query);
} catch (Exception $e) {
echo 'Caught exception: ' . $e->getMessage();
exit();
}
if ($results) {
while ($row = $results->fetchArray()) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
} else {
echo "Error in fetch ".$sqlite_db->lastErrorMsg();
}
break;
}
}
// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query = "SELECT COUNT(*) FROM users;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
$number_of_rows = mysqli_fetch_row( $result )[0];
mysqli_close($GLOBALS["___mysqli_ston"]);
?>
3、分析网页源代码
(1)代码概述
MEDIUM 级别的后端逻辑在 LOW 级别的基础上,主要增加了对用户输入的转义处理,其核心流程如下:
- 获取用户输入 :从
$_POST数组中获取用户通过表单提交的id参数,提交方式由 GET 改为 POST。 - 转义特殊字符 :使用
mysqli_real_escape_string()函数对$id参数中的特殊字符(如单引号'、双引号"、反斜杠\等)进行转义,旨在防止这些字符破坏 SQL 语句的结构。 - 构建并执行查询 :将转义后的
$id变量直接拼接到 SQL 查询语句SELECT first_name, last_name FROM users WHERE user_id = $id;中并执行。 - 输出查询结果 :将查询到的用户信息(
first_name,last_name)格式化输出到页面。
关键变化 :与 LOW 级别不同,MEDIUM 级别的查询语句为 user_id = $id,变量 $id 没有被单引号包裹。这意味着此处是数字型 SQL 注入,而非字符型。
(2)漏洞分析
尽管引入了转义函数,MEDIUM 级别仍然存在以下安全漏洞:
① 漏洞一 ------ 前端下拉菜单可被绕过
问题分析 :前端界面使用下拉选择框(<select>)限制用户只能选择预设的 id 值(如 1, 2, 3, 4, 5)。然而,这种限制仅作用于浏览器端,属于用户体验层面的控制 ,而非安全屏障。攻击者可以轻松使用 Burp Suite、Postman、浏览器开发者工具等代理或调试工具,在 HTTP 请求被发送到服务器前拦截并修改 id 参数,将其替换为任意恶意值,从而完全绕过前端限制。
② 漏洞二 ------ 转义函数对数字型注入无效
问题分析 :这是 MEDIUM 级别最核心的漏洞。mysqli_real_escape_string() 函数的作用是转义字符串中的特殊字符,使其在 SQL 语句中失去特殊含义(例如,将单引号 ' 转义为 \')。然而,MEDIUM 级别的查询语句为 user_id = $id,变量 $id 没有被单引号包裹 。这意味着应用程序期望 $id 是一个数字,直接将其作为数值进行拼接。
- 数字型注入原理 :在数字型注入中,攻击者注入的 Payload(如
1 OR 1=1)本身就是有效的 SQL 表达式的一部分,不需要通过单引号来"闭合"字符串。因此,即使mysqli_real_escape_string()函数被执行,它对数字本身(如1、2)或 SQL 运算符(如OR、=)也不起任何转义作用。 - 攻击示例 :攻击者可以提交
id=1 OR 1=1,最终执行的 SQL 语句为SELECT ... WHERE user_id = 1 OR 1=1。由于1=1恒为真,此查询将返回users表中的所有记录,成功实现注入。
③ 漏洞三 ------ 错误信息直接回显
问题分析 :与 LOW 级别相同,当 SQL 语句执行出错时(例如,语法错误、表不存在等),mysqli_error() 函数返回的详细数据库错误信息会直接输出到前端页面。这为攻击者提供了宝贵的信息泄露渠道。攻击者可以利用这些错误信息来推断后端数据库的类型、版本、表结构等敏感信息,从而辅助构造更精准的注入 Payload。
4、操作步骤
(1)抓包修改 id 参数
本步骤演示如何绕过前端下拉菜单的限制,通过代理工具(如 Burp Suite)直接修改 HTTP 请求参数,实现数字型 SQL 注入攻击。
-
开启代理拦截 :启动 Burp Suite,配置浏览器代理,并确保 Burp Suite 的 Intercept is on(拦截开启)状态。
-
触发正常请求 :在 DVWA 的 SQL Injection 页面(安全级别设置为 Medium ),从下拉菜单中选择一个预设值(例如
1),然后点击 Submit 按钮提交查询。 -
拦截 HTTP 请求 :Burp Suite 将拦截到浏览器发出的 POST 请求。请求示例如下:
httpPOST /vulnerabilities/sqli/ HTTP/1.1 Host: dvwa.cc Cookie: PHPSESSID=xxx; security=medium id=1&Submit=Submit -
构造并修改 Payload :在 Burp Suite 的拦截界面中,找到
id参数,将其值从1修改为恶意 SQL 注入 Payload。由于 MEDIUM 级别是数字型注入,Payload 中不需要使用单引号 。例如,修改为:httpid=1 UNION SELECT user, password FROM users #&Submit=Submit -
放行请求并观察结果 :点击 Forward 按钮放行修改后的请求。观察浏览器返回的页面,如果注入成功,页面将在原本显示用户姓名的位置,返回
users表中的用户名和密码哈希值。

(2)数字型注入 Payload
由于 MEDIUM 级别是数字型注入,Payload 中不需要使用单引号进行闭合。下表列举了几个常用的数字型注入 Payload 及其攻击效果:
| Payload | 效果 |
|---|---|
1 UNION SELECT database(), version()&Submit=Submit |
获取当前数据库名称和数据库版本信息。 |
1 UNION SELECT user, password FROM users #&Submit=Submit |
直接提取 users 表中的用户名和密码哈希值。 |
1 UNION SELECT 1, CONVERT(table_name USING latin1) FROM information_schema.tables WHERE table_schema=database() #&Submit=Submit |
获取当前数据库中的所有表名。 |
使用说明:
- 将上述任一 Payload 作为
id参数的值,通过代理工具(如 Burp Suite)修改并发送请求。 - 观察页面返回结果,验证注入是否成功,并获取相应的敏感信息。
5、AI视角下的 SQL 注入检测
(1)转义函数的智能化增强
传统的 mysqli_real_escape_string() 函数仅能转义特殊字符(如单引号、反斜杠),对于数字型注入或经过编码混淆的攻击往往无能为力。AI 技术可以从以下方面辅助构建更智能的转义与验证机制:
- 上下文感知转义 :AI 模型可以分析 SQL 查询语句的上下文结构,动态判断用户输入是否处于字符串引号内。对于数字型上下文(如
WHERE user_id = $id),即使输入包含单引号也无需转义,从而避免误伤正常输入;而对于字符型上下文,则自动启用严格的转义策略。 - 注入模式识别 :通过机器学习模型识别输入中是否包含典型的 SQL 注入模式(如
UNION SELECT、OR 1=1、' OR '1'='1等关键字组合),即使这些模式经过简单的变形或拆分,AI 也能基于语义特征进行检测。 - 编码与混淆检测:攻击者常对注入 Payload 进行 URL 编码、十六进制编码、Unicode 编码等处理以绕过传统检测。AI 模型可以学习这些编码模式,自动解码并分析原始内容,识别隐藏在编码背后的恶意 SQL 片段。
(2)基于请求上下文的检测
MEDIUM 级别将表单提交方式改为 POST,并引入了前端限制,这为基于上下文的 AI 检测提供了新的维度:
- 请求方法差异分析:正常用户操作通常通过前端表单(POST)提交,而自动化攻击工具可能直接发送 GET 请求或修改请求方法。AI 可以监控请求方法(GET/POST)与页面功能的匹配度,识别异常的方法使用模式。
- 参数值异常检测 :结合请求上下文(如表单字段类型、预期输入范围),AI 可以判断参数值是否异常。例如,对于下拉菜单的
id参数,正常值应为预设的 1-5,若收到1 UNION SELECT...这类明显包含 SQL 语法的值,则可判定为高风险。 - 请求-响应行为关联分析 :AI 可以关联分析单个请求与服务器响应的关系。例如,正常查询
id=1应返回特定用户信息;而注入攻击id=1 OR 1=1可能导致返回大量数据或异常错误信息。通过建立正常响应的基线模型,AI 能够识别偏离基线的异常数据泄露行为。
6、MEDIUM级别------小结
MEDIUM 级别在 LOW 级别的基础上引入了两种看似有效的防护措施:后端使用 mysqli_real_escape_string() 函数对用户输入进行转义,前端使用下拉选择框限制用户输入,并将表单提交方式改为 POST。然而,这些防护措施仍存在明显缺陷,无法从根本上阻止 SQL 注入攻击。
核心安全问题总结:
- 前端限制可被绕过 :前端下拉菜单仅是一种用户体验层面的限制,并非安全屏障。攻击者可以轻松使用 Burp Suite、Postman 或浏览器开发者工具等代理或调试工具,在 HTTP 请求被发送到服务器前拦截并修改
id参数,将其替换为任意恶意值,从而完全绕过前端限制。 - 转义函数对数字型注入无效 :
mysqli_real_escape_string()函数的作用是转义字符串中的特殊字符(如单引号、反斜杠)。然而,MEDIUM 级别的查询语句为user_id = $id,变量$id未被单引号包裹,属于数字型 SQL 注入。对于数字型注入,转义函数无法对数字本身或 SQL 运算符(如OR、=)产生任何转义效果,攻击者可以直接注入数字型 Payload(如1 OR 1=1)进行攻击。 - 错误信息直接回显 :与 LOW 级别相同,当 SQL 语句执行出错时,详细的数据库错误信息会通过
mysqli_error()直接输出到前端页面。这为攻击者提供了宝贵的信息泄露渠道,使其能够推断数据库的类型、版本、表结构等敏感信息,辅助构造更精准的注入 Payload。
攻击注意事项:
由于 MEDIUM 级别使用 POST 方法提交表单,攻击者在构造 Payload 时,必须在 Payload 末尾保留 &Submit=Submit 参数,才能成功触发查询。例如:id=1 UNION SELECT user, password FROM users #&Submit=Submit。
安全启示:
MEDIUM 级别的案例表明,单纯依赖转义函数和前端限制无法有效防御 SQL 注入。转义函数仅适用于字符型注入场景,对数字型注入无效;前端限制则完全依赖于客户端的可信性,极易被绕过。在实际开发中,应优先采用参数化查询(预编译语句)等根本性的安全编码实践,并结合严格的输入验证、最小权限原则和错误信息模糊化处理,构建多层次的安全防御体系。
四、HIGH 级别 ------ LIMIT 限制与 SESSION 会话
1、漏洞描述
HIGH 级别在 MEDIUM 级别的基础上,进一步引入了两项看似更严格的防护措施:在 SQL 查询语句末尾添加了 LIMIT 1 限制,并将用户提交的 id 参数存储在 $_SESSION 会话变量中。然而,这些措施依然未能从根本上消除 SQL 注入漏洞。
核心防护机制:
- 参数存储于会话 :用户提交的
id参数不再直接从$_GET或$_POST获取,而是先存储在$_SESSION['id']中,后续查询从会话中读取该值。这增加了攻击者直接修改请求参数的难度。 - 查询结果限制 :SQL 查询语句末尾添加了
LIMIT 1子句,旨在限制每次查询只返回一条记录,试图阻止攻击者通过UNION SELECT等方式一次性提取大量数据。 - 错误信息模糊化:当 SQL 语句执行出错时,不再输出详细的数据库错误信息,而是返回通用的错误提示 "Something went wrong.",旨在减少信息泄露。
核心安全问题:
尽管引入了上述防护,HIGH 级别仍然存在可被利用的安全漏洞:
LIMIT 1限制可被绕过 :LIMIT 1子句仅限制查询结果返回的记录数,但无法阻止攻击者执行恶意的 SQL 语句。攻击者可以通过 SQL 注释符(如#或--)将LIMIT 1注释掉,从而绕过该限制。例如,注入1' UNION SELECT database(), version() #后,实际执行的查询变为SELECT ... WHERE user_id = '1' UNION SELECT database(), version() #' LIMIT 1;,#之后的内容被注释,LIMIT 1失效。- 错误信息模糊化但注入点仍在:虽然错误信息不再泄露具体的数据库错误细节,降低了攻击者利用错误信息进行盲注的难度,但 SQL 注入漏洞本身依然存在。攻击者仍可通过布尔盲注、时间盲注等技术,根据页面返回的正常/异常状态差异来推断信息。
- 会话存储并非安全屏障 :将参数存储在
$_SESSION中只是改变了参数的来源,并未对参数值本身进行任何安全处理(如验证、过滤或参数化)。如果攻击者能够通过其他方式(如会话固定、会话劫持)操纵$_SESSION['id']的值,注入依然可能发生。
攻击影响:
即使有 LIMIT 1 和模糊化的错误信息,攻击者仍然可以:
- 通过注释符绕过
LIMIT 1限制,执行UNION SELECT查询提取数据库名、表名、列名等敏感信息。 - 利用布尔逻辑(如
AND 1=1/AND 1=2)判断注入点是否存在,并进行盲注攻击。 - 在特定条件下,可能通过子查询、堆叠查询等技术进行更深入的数据窃取或破坏。
安全启示:
HIGH 级别的案例再次证明,单纯依靠添加 LIMIT、模糊错误信息或改变参数存储位置等"外围"防护措施,无法根治 SQL 注入漏洞。最有效的防御手段仍然是采用参数化查询(预编译语句),并结合严格的输入验证、输出编码和最小权限原则,从应用程序逻辑层面杜绝 SQL 注入的可能性。
2、查看网页源代码
php
<?php
if( isset( $_SESSION [ 'id' ] ) ) {
// Get input
$id = $_SESSION[ 'id' ];
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
#print $query;
try {
$results = $sqlite_db_connection->query($query);
} catch (Exception $e) {
echo 'Caught exception: ' . $e->getMessage();
exit();
}
if ($results) {
while ($row = $results->fetchArray()) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
} else {
echo "Error in fetch ".$sqlite_db->lastErrorMsg();
}
break;
}
}
?>
3、分析网页源代码
(1)代码概述
HIGH 级别的后端逻辑在 MEDIUM 级别的基础上,主要增加了两项防护措施:将用户提交的 id 参数存储在 $_SESSION 会话变量中,并在 SQL 查询语句末尾添加了 LIMIT 1 限制。其核心流程如下:
- 从会话获取输入 :不再直接从
$_GET或$_POST获取用户输入,而是从$_SESSION['id']中读取id参数。这要求攻击者必须先通过正常流程提交一次表单,将id值存入会话,才能进行后续注入尝试。 - 构建并执行查询 :将
$_SESSION['id']中的值直接拼接到 SQL 查询语句SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;中并执行。查询语句使用单引号包裹$id变量,属于字符型 SQL 注入。 - 限制查询结果 :在查询语句末尾添加了
LIMIT 1子句,旨在限制每次查询只返回一条记录,试图阻止攻击者通过UNION SELECT等方式一次性提取大量数据。 - 模糊化错误信息:当 SQL 语句执行出错时,不再输出详细的数据库错误信息,而是返回通用的错误提示 "Something went wrong.",旨在减少信息泄露,增加攻击者进行错误型注入的难度。
- 输出查询结果 :将查询到的用户信息(
first_name,last_name)格式化输出到页面。
关键变化:与 MEDIUM 级别相比,HIGH 级别的主要变化在于:
- 参数来源 :从
$_POST['id']改为$_SESSION['id'],增加了攻击者直接修改请求参数的难度。 - 结果限制 :增加了
LIMIT 1子句,试图限制数据泄露量。 - 错误处理:错误信息从详细的数据库错误变为通用的 "Something went wrong."。
(2)漏洞分析
尽管引入了会话存储和 LIMIT 1 限制,HIGH 级别仍然存在以下安全漏洞:
① 漏洞一 ------ LIMIT 1 可被注释绕过
问题分析 :LIMIT 1 子句仅限制查询结果返回的记录数,但无法阻止攻击者执行恶意的 SQL 语句。攻击者可以在注入 Payload 的末尾添加 SQL 注释符(如 # 或 -- ),将 LIMIT 1 注释掉,从而绕过该限制。
攻击示例:
- Payload :
1' UNION SELECT user, password FROM users # - 实际执行的 SQL :
SELECT first_name, last_name FROM users WHERE user_id = '1' UNION SELECT user, password FROM users #' LIMIT 1; - 结果 :
#之后的内容(包括' LIMIT 1;)被注释,UNION SELECT查询得以完整执行,返回users表中的所有用户名和密码,成功绕过LIMIT 1限制。
绕过原理 :SQL 注释符(# 或 -- )会使其后的所有内容被数据库引擎忽略。攻击者利用这一特性,在 Payload 末尾添加注释符,使原本用于限制结果的 LIMIT 1 子句失效。
② 漏洞二 ------ 字符型注入
问题分析 :查询语句 user_id = '$id' 使用单引号将变量包裹,这属于典型的字符型 SQL 注入。攻击者需要先闭合前面的单引号,才能插入并执行自己的恶意 SQL 代码。虽然 HIGH 级别引入了会话存储和 LIMIT 1,但并未对用户输入进行任何过滤、转义或参数化处理,字符型注入漏洞依然存在。
攻击示例:
- Payload :
1' OR '1'='1' # - 实际执行的 SQL :
SELECT first_name, last_name FROM users WHERE user_id = '1' OR '1'='1' #' LIMIT 1; - 结果 :由于
OR '1'='1'条件恒为真,查询将返回users表中的第一条记录(受LIMIT 1限制)。虽然LIMIT 1仍然生效,但攻击者已成功注入并改变了查询逻辑。
安全影响 :字符型注入漏洞的存在,使得攻击者仍然可以通过闭合单引号、插入恶意 SQL 代码、并用注释符注释掉后续内容(包括 LIMIT 1)的方式,执行任意 SQL 查询,从而提取敏感数据或进行其他恶意操作。
4、操作步骤
(1)使用注释符绕过 LIMIT 1
HIGH 级别在 SQL 查询语句末尾添加了 LIMIT 1 子句,旨在限制每次查询只返回一条记录,试图阻止攻击者通过 UNION SELECT 等方式一次性提取大量数据。然而,攻击者可以利用 SQL 注释符(如 # 或 -- )将 LIMIT 1 注释掉,从而绕过该限制。
操作步骤:
-
正常提交一次查询 :首先,在 DVWA 的 SQL Injection 页面(安全级别设置为 High )中,正常输入一个
id值(例如1)并提交。这一步的目的是将id参数存入$_SESSION会话变量,因为 HIGH 级别从会话中读取该值。 -
构造绕过 Payload :在输入框中输入以下 Payload:
1' UNION SELECT user, password FROM users # -
提交 Payload 并观察结果 :点击 Submit 按钮提交查询。如果注入成功,页面将在原本显示用户姓名的位置,返回
users表中的所有用户名和经过 MD5 加密的密码哈希值。
原理分析:
-
原始 SQL 语句 :
SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1; -
注入后的 SQL 语句 :
SELECT first_name, last_name FROM users WHERE user_id = '1' UNION SELECT user, password FROM users #' LIMIT 1; -
绕过效果 :
#是 MySQL 的单行注释符,它会将其后的所有内容(包括' LIMIT 1;)注释掉。因此,实际执行的查询变为:sqlSELECT first_name, last_name FROM users WHERE user_id = '1' UNION SELECT user, password FROM users该查询会执行
UNION SELECT操作,返回users表中的所有用户数据,成功绕过了LIMIT 1的限制。
注意事项:
- 由于 HIGH 级别从
$_SESSION['id']读取参数,攻击者必须先通过正常流程提交一次表单,将id值存入会话,才能进行后续注入尝试。 - 除了
#,也可以使用--(注意后面有一个空格)作为注释符,效果相同。 - 即使有
LIMIT 1限制,攻击者仍然可以通过注释符轻松绕过,这再次证明了仅依靠外围防护措施无法有效防御 SQL 注入。

(2)其他 HIGH 级别 Payload
除了上述绕过 LIMIT 1 的 Payload 外,攻击者还可以利用其他常见的 SQL 注入 Payload 在 HIGH 级别下进行攻击。下表列举了几个常用的 HIGH 级别 Payload 及其攻击效果:
| Payload | 效果 |
|---|---|
1' UNION SELECT database(), version() # |
获取当前数据库名称和数据库版本信息。 |
1' UNION SELECT user, password FROM users # |
直接提取 users 表中的用户名和经过 MD5 加密的密码哈希值。 |
1' UNION SELECT 1, CONVERT(table_name USING latin1) FROM information_schema.tables WHERE table_schema=database() # |
获取当前数据库中的所有表名。 |
使用说明:
- 前提条件 :由于 HIGH 级别从
$_SESSION['id']读取参数,攻击者必须先通过正常流程提交一次表单(例如输入1并提交),将id值存入会话,才能进行后续注入尝试。 - 构造并提交 Payload :在输入框中输入上述任一 Payload,然后点击 Submit 按钮提交查询。
- 观察结果:如果注入成功,页面将在原本显示用户姓名的位置,返回相应的查询结果(如数据库名、版本信息、用户名和密码哈希值、表名等)。
攻击原理:
所有 Payload 均利用了字符型 SQL 注入漏洞,并通过 SQL 注释符 # 注释掉了查询语句末尾的 LIMIT 1 子句,从而绕过结果限制。例如,Payload 1' UNION SELECT database(), version() # 会闭合前面的单引号,插入 UNION SELECT 查询,并用 # 注释掉后续的 ' LIMIT 1;,最终执行完整的 UNION 查询以提取敏感信息。
安全启示:
HIGH 级别的案例再次表明,单纯依靠 LIMIT 限制、会话存储或错误信息模糊化等外围防护措施,无法从根本上消除 SQL 注入漏洞。最有效的防御手段仍然是采用参数化查询(预编译语句),并结合严格的输入验证、输出编码和最小权限原则,从应用程序逻辑层面杜绝 SQL 注入的可能性。
5、AI视角下的 SQL 注入检测
(1)LIMIT 绕过的智能检测
HIGH 级别通过 LIMIT 1 子句限制查询结果,但攻击者可通过 SQL 注释符(如 #、--、/* */)将其注释掉,从而绕过限制。AI 技术可从以下维度辅助检测此类绕过行为:
- 注释符检测 :AI 模型可识别输入中是否包含 SQL 注释符及其变体。除了常见的
#、--、/* */,还需检测经过编码、混淆或嵌套的注释形式(如%23、--+、/**/),防止攻击者通过变形绕过简单规则。 - 语句完整性分析 :AI 可对最终执行的 SQL 语句进行语法和语义分析,判断其是否被异常截断或篡改。例如,原始查询末尾应有
LIMIT 1,若检测到该子句被注释或删除,且查询结构发生改变(如增加了UNION SELECT),则可判定为潜在注入。 - 结果集异常检测:结合业务逻辑,AI 可监控查询返回的结果数量。对于明确设计为单条记录查询的接口(如根据 ID 查询用户),若返回结果数量超过 1 条,则可能存在注入绕过行为。
(2)基于查询结果的异常检测
AI 可通过分析查询结果的多个维度,识别潜在的 SQL 注入攻击,即使攻击者绕过了传统的防护措施。
- 结果数量异常检测 :对于明确使用
LIMIT 1的查询,AI 可监控返回的记录数。若实际返回多条记录,则可能表明LIMIT子句被绕过,存在注入风险。AI 可结合历史基线,动态学习不同查询的预期结果数量范围。 - 数据内容异常检测:AI 可分析返回数据的语义和格式是否符合预期。例如,用户查询接口本应返回姓名等基本信息,若结果中出现了密码哈希、数据库版本、系统表名等敏感信息,则可判定为数据泄露异常。模型可通过训练学习正常数据的分布特征,识别偏离该分布的异常内容。
- 响应时间异常检测 :针对时间盲注,AI 可监控查询的响应时间。攻击者常利用
SLEEP()、BENCHMARK()等函数构造延迟,以根据响应时间差异推断信息。AI 可建立正常请求的响应时间基线,并识别出显著超出基线的异常请求,即使其不包含明显的恶意关键字。
技术实现路径:
- 多模型融合:结合规则引擎(用于检测已知模式)、机器学习模型(用于识别未知变种)和深度学习模型(用于语义理解),构建分层检测体系。
- 上下文关联:将单个请求的检测结果与用户会话历史、IP 信誉、访问频率等上下文信息关联,进行综合风险评估。
- 实时学习与迭代:利用在线学习机制,持续从新的攻击样本和正常流量中学习,动态更新检测模型,以应对不断演变的注入手法。
通过上述 AI 增强的检测手段,可以在传统规则匹配的基础上,更智能、更精准地识别 HIGH 级别中利用注释符绕过 LIMIT、基于结果集和时间延迟的复杂 SQL 注入攻击,提升整体安全防御的深度和适应性。
6、HIGH级别------小结
HIGH 级别在 MEDIUM 级别的基础上,进一步引入了 LIMIT 1 限制和错误信息模糊化等防护措施,但这些措施仍存在明显缺陷,无法从根本上消除 SQL 注入漏洞。
核心安全问题总结:
LIMIT 1限制可被注释符绕过 :虽然查询语句末尾添加了LIMIT 1子句以限制返回记录数,但攻击者可以通过 SQL 注释符(如#或--)轻松将其注释掉,从而绕过该限制,执行完整的UNION SELECT查询以提取大量数据。- 字符型注入漏洞依然存在 :查询语句
user_id = '$id'仍使用单引号包裹变量,属于典型的字符型 SQL 注入。攻击者只需闭合单引号并插入恶意 SQL 代码,即可实施注入攻击。 - 错误信息模糊化但注入点仍在:虽然错误信息不再泄露具体的数据库结构细节,降低了攻击者利用错误信息进行盲注的难度,但 SQL 注入漏洞本身并未被修复,攻击者仍可通过布尔盲注、时间盲注等技术进行信息推断。
- 会话存储并非安全屏障 :将参数存储在
$_SESSION中只是改变了参数的来源,并未对参数值本身进行任何安全处理。如果攻击者能够通过其他方式操纵会话数据,注入依然可能发生。
安全启示:
HIGH 级别的案例再次证明,单纯依靠添加 LIMIT 限制、模糊错误信息或改变参数存储位置等"外围"防护措施,无法根治 SQL 注入漏洞。这些措施只能增加攻击难度,但无法消除漏洞本身。最有效的防御手段仍然是采用参数化查询(预编译语句),并结合严格的输入验证、输出编码和最小权限原则,从应用程序逻辑层面彻底杜绝 SQL 注入的可能性。
五、Impossible 级别 ------ 参数化查询与 CSRF Token
1、漏洞描述
Impossible 级别是 DVWA SQL 注入模块的最高安全级别,它通过多重防护机制从根本上杜绝了 SQL 注入漏洞。与之前级别不同,Impossible 级别不再演示漏洞利用,而是展示了如何通过安全编码实践彻底防御 SQL 注入攻击。
核心安全改进:
- CSRF Token 验证 :通过
checkToken()函数验证请求的合法性,防止跨站请求伪造攻击,确保每个请求都来自合法的用户会话。 - 数字类型验证 :使用
is_numeric()函数对用户输入的id参数进行严格检查,确保输入为有效数字,从源头过滤非数字字符。 - PDO 参数化查询 :采用 PDO(PHP Data Objects)的预编译语句,通过
prepare()方法预定义 SQL 查询结构,再使用bindParam()方法将用户输入作为参数绑定,实现数据与代码的完全分离。 - 结果数量限制 :在执行查询后,通过
rowCount() == 1检查确保查询结果仅返回一条记录,防止攻击者通过 UNION 查询等方式提取多条数据。 - LIMIT 1 子句 :在 SQL 查询语句层面添加
LIMIT 1限制,进一步确保每次查询最多只返回一条记录。 - Token 刷新机制 :每次请求后通过
generateSessionToken()生成新的会话 Token,防止 Token 重用和会话固定攻击。
安全效果评估:
Impossible 级别通过上述多层防护机制,构建了纵深防御体系。参数化查询从根本上消除了 SQL 注入的可能性,而 CSRF Token 验证、输入类型检查和结果数量限制则提供了额外的安全冗余。即使攻击者尝试注入恶意 SQL 代码,也会被这些防护机制有效拦截,无法对数据库造成任何影响。
2、查看网页源代码
php
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$id = $_GET[ 'id' ];
// Was a number entered?
if(is_numeric( $id )) {
$id = intval ($id);
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check the database
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();
// Make sure only 1 result is returned
if( $data->rowCount() == 1 ) {
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
break;
case SQLITE:
global $sqlite_db_connection;
$stmt = $sqlite_db_connection->prepare('SELECT first_name, last_name FROM users WHERE user_id = :id LIMIT 1;' );
$stmt->bindValue(':id',$id,SQLITE3_INTEGER);
$result = $stmt->execute();
$result->finalize();
if ($result !== false) {
// There is no way to get the number of rows returned
// This checks the number of columns (not rows) just
// as a precaution, but it won't stop someone dumping
// multiple rows and viewing them one at a time.
$num_columns = $result->numColumns();
if ($num_columns == 2) {
$row = $result->fetchArray();
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
break;
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
3、分析网页源代码
(1)代码概述
Impossible 级别采用了纵深防御策略,通过多层安全机制协同工作,从根本上杜绝 SQL 注入漏洞:
- CSRF Token 验证 :通过
checkToken()函数验证请求的合法性,防止跨站请求伪造攻击,确保每个请求都来自合法的用户会话。 - 数字类型验证 :使用
is_numeric($id)函数对用户输入的id参数进行严格检查,确保输入为有效数字,从源头过滤非数字字符。 - 类型转换 :通过
intval($id)将验证通过的数字输入转换为整数类型,进一步确保数据格式的规范性。 - PDO 参数化查询 :采用 PDO(PHP Data Objects)的预编译语句,通过
prepare()方法预定义 SQL 查询结构,再使用bindParam()方法将用户输入作为参数绑定,实现数据与代码的完全分离。 - 结果数量限制 :在执行查询后,通过
rowCount() == 1检查确保查询结果仅返回一条记录,防止攻击者通过 UNION 查询等方式提取多条数据。 - LIMIT 1 子句 :在 SQL 查询语句层面添加
LIMIT 1限制,进一步确保每次查询最多只返回一条记录。 - Token 刷新机制 :每次请求后通过
generateSessionToken()生成新的会话 Token,防止 Token 重用和会话固定攻击。
(2)安全分析
下表详细分析了 Impossible 级别各项安全措施的实现方式及其防御效果:
| 安全措施 | 实现方式 | 防御效果 |
|---|---|---|
| CSRF Token | checkToken() 函数验证请求中的 Token 与会话 Token 是否匹配 |
防止跨站请求伪造攻击,确保请求来源的合法性 |
| 数字类型验证 | is_numeric() 函数检查输入是否为有效数字 |
从源头拒绝非数字输入,过滤潜在的恶意字符 |
| 类型转换 | intval() 函数将输入转换为整数 |
确保输入数据格式规范,消除类型混淆风险 |
| PDO 参数化查询 | prepare() 预编译 SQL 语句 + bindParam() 绑定参数 |
彻底杜绝 SQL 注入,实现数据与代码的完全分离 |
| 结果数量限制 | rowCount() == 1 检查返回结果数量 |
防止数据批量泄露,即使有漏洞也只能获取单条记录 |
| LIMIT 1 | SQL 语句中添加 LIMIT 1 子句 |
数据库层面双重限制返回数量,增加攻击难度 |
(3)为什么 Impossible 级别是安全的?
Impossible 级别的安全性源于其多层次、纵深防御的设计理念,其中最关键的是参数化查询机制:
- SQL 语句与数据分离 :通过 PDO 的预编译语句,SQL 查询结构在预处理阶段就已确定,用户输入仅作为参数传入,被数据库视为纯数据而非可执行代码。即使输入中包含
'、OR、UNION等 SQL 关键字,也不会被数据库解析执行。 - 输入验证与类型强制 :
is_numeric()和intval()的组合确保了输入必须是数字,从根本上杜绝了字符型注入的可能性。即使攻击者尝试注入数字型 Payload,参数化查询机制也能确保其仅作为数据值处理。 - 多重结果限制 :
LIMIT 1子句和rowCount() == 1检查形成了双重保障,即使存在其他未知漏洞,也能最大程度限制数据泄露的范围。 - CSRF 防护:Token 验证机制防止了跨站请求伪造,确保只有合法用户发起的请求才能被处理。
一句话总结:Impossible 级别通过 CSRF Token 验证、严格的输入验证、PDO 参数化查询以及多重结果限制,构建了纵深防御体系,使 SQL 注入攻击在理论上变得"不可能"。
4、操作步骤
Impossible 级别通过多重安全机制彻底防御了 SQL 注入攻击。以下步骤演示了在该级别下,正常查询与恶意注入尝试的不同结果,直观展示了安全防护的有效性。
(1)正常查询(数字输入)
- 设置安全级别 :在 DVWA 左侧菜单栏的 "DVWA Security" 页面中,将安全级别设置为 Impossible。
- 进入 SQL 注入页面:点击左侧菜单的 "SQL Injection" 进入测试页面。
- 执行正常查询 :在输入框中输入数字
1,然后点击 Submit 按钮。 - 观察结果:页面正常显示 ID 为 1 的用户信息(如 First name: admin, Surname: admin)。这表明在安全参数(数字输入)下,应用程序功能正常。

(2)尝试 SQL 注入(被拒绝)
-
构造注入 Payload :在输入框中输入以下典型的 SQL 注入 Payload:
1' UNION SELECT user, password FROM users -
提交并观察 :点击 Submit 按钮提交查询。
-
预期结果 :页面无任何数据输出 (或仅显示空白)。这是因为:
is_numeric()函数检测到输入中包含非数字字符(单引号'和字母),判定为无效输入。- 程序在数字验证阶段即拒绝执行后续查询,恶意 Payload 根本不会到达数据库执行层。
- 参数化查询(PDO prepared statement)机制即使输入通过验证,也会将整个 Payload 视为一个完整的字符串参数值,而不会将其解析为 SQL 代码。

(3)尝试非数字输入(被拒绝)
-
输入非数字内容 :在输入框中输入纯字母字符串,例如:
abc -
提交并观察 :点击 Submit 按钮提交查询。
-
预期结果 :页面同样无任何数据输出 。
is_numeric()函数直接拒绝了非数字输入,程序不会执行任何数据库查询操作。

安全机制总结
通过上述操作可以看出,Impossible 级别的安全设计实现了纵深防御:
- 输入验证层 :
is_numeric()严格限制了输入必须为数字,从源头过滤了绝大多数恶意字符。 - 参数化查询层:PDO 预编译语句确保用户输入始终被当作数据值处理,与 SQL 代码逻辑完全分离。
- 结果限制层 :
LIMIT 1和rowCount() == 1双重保障,即使出现极端情况,也最大程度限制了数据泄露范围。 - 请求验证层:CSRF Token 机制确保了请求的合法性,防止了跨站请求伪造攻击。
因此,在 Impossible 级别下,无论是字符型还是数字型 SQL 注入攻击,都会被上述机制有效拦截,无法对数据库产生任何影响。
5、Impossible级别------小结
Impossible 级别通过多层次、纵深防御的安全设计,从根本上杜绝了 SQL 注入漏洞:
- CSRF Token 验证:防止跨站请求伪造攻击,确保请求来源的合法性。
- 严格的输入验证 :使用
is_numeric()函数检查输入是否为有效数字,并结合intval()进行类型转换,从源头过滤非数字字符。 - 参数化查询(预编译语句) :采用 PDO 的
prepare()和bindParam()方法,实现 SQL 语句结构与用户数据的完全分离,彻底消除 SQL 注入的可能性。 - 结果数量限制 :通过
rowCount() == 1检查确保查询仅返回单条记录,防止数据批量泄露。 - Token 刷新机制:每次请求后生成新的会话 Token,防止 Token 重用与会话固定攻击。
一句话总结:Impossible 级别通过 CSRF Token 验证、严格的输入验证、参数化查询以及多重结果限制,构建了纵深防御体系,使 SQL 注入攻击在理论上变得"不可能"。
6、AI视角下的 SQL 注入防御
Impossible 级别的参数化查询虽然能有效抵御传统 SQL 注入攻击,但结合人工智能技术可以在以下方面实现更深层次的防御增强:
(1)智能 SQL 注入检测
实时流量分析:利用 AI 模型持续监控数据库查询流量,通过模式识别技术自动发现异常查询行为,及时预警潜在的攻击尝试。
查询模式学习:基于历史正常查询数据建立行为基线模型,通过机器学习算法学习应用的正常查询模式,智能检测偏离基线的异常查询请求。
隐式注入检测:针对经过编码、混淆或变形处理的注入攻击,AI 能够识别隐藏在正常输入中的恶意意图,有效检测传统规则引擎难以发现的隐式注入尝试。
(2)自适应查询保护
动态白名单机制:AI 系统通过学习应用的正常查询模式,自动构建动态查询白名单,将符合正常业务逻辑的查询纳入信任范围。
异常查询智能阻断:当检测到不在白名单范围内且具有高风险特征的查询时,系统可自动阻断或进入人工审核流程,防止潜在的攻击执行。
查询复杂度分析:AI 模型能够分析查询语句的结构复杂度、嵌套深度等特征,识别异常复杂的查询模式(可能为精心构造的注入攻击),提供额外的风险预警。)
六、LOW vs MEDIUM vs HIGH vs Impossible:防御演进对比
1、防护策略演进对比
| 防护维度 | LOW 级别 | MEDIUM 级别 | HIGH 级别 | Impossible 级别 |
|---|---|---|---|---|
| 输入过滤 | ❌ 无任何过滤 | ⚠️ 使用转义函数(mysqli_real_escape_string()) |
❌ 无过滤(依赖 SESSION 存储) | ✅ 严格的类型验证(is_numeric())与类型转换(intval()) |
| 注入类型 | 字符型(单引号包裹) | 数字型(无引号包裹) | 字符型(单引号包裹) | 无(仅允许数字输入) |
| 请求方法 | GET/POST | POST | SESSION(需先提交一次表单) | GET + CSRF Token 验证 |
| 查询限制 | ❌ 无限制 | ❌ 无限制 | ⚠️ 使用 LIMIT 1(可被注释绕过) |
✅ LIMIT 1 + rowCount() == 1 双重限制 |
| 错误信息 | ❌ 直接回显详细数据库错误 | ❌ 直接回显详细数据库错误 | ⚠️ 通用错误提示("Something went wrong.") | ✅ 不向用户输出任何数据库错误信息 |
| 核心防御策略 | 无任何防护 | 弱防护(转义函数 + 前端限制) | 弱防护(SESSION + LIMIT + 错误模糊化) | 参数化查询(PDO prepared statement)+ CSRF Token |
2、攻击面变化分析
| 攻击类型 | LOW | MEDIUM | HIGH | Impossible |
|---|---|---|---|---|
| 字符型注入 | ✅ 可直接利用(单引号闭合) | ❌ 被转义函数阻止 | ✅ 可绕过(闭合单引号 + 注释) | ❌ 被参数化查询彻底阻止 |
| 数字型注入 | ✅ 可直接利用 | ✅ 可绕过(转义对数字无效) | ❌ 不适用(字符型上下文) | ❌ 被参数化查询彻底阻止 |
| UNION 查询 | ✅ 可直接利用 | ✅ 可绕过(数字型注入) | ✅ 需注释 LIMIT 1 才能利用 |
❌ 被参数化查询彻底阻止 |
| 错误信息利用 | ✅ 可直接利用(信息泄露) | ✅ 可直接利用(信息泄露) | ❌ 仅通用提示(信息有限) | ❌ 无错误信息输出 |
| CSRF 攻击 | ✅ 可行(无防护) | ✅ 可行(无防护) | ✅ 可行(无防护) | ❌ 被 CSRF Token 验证阻止 |
3、SQL 注入防御的核心原则
| 原则 | 说明 | 在 DVWA 中的实现级别 |
|---|---|---|
| 参数化查询 | 使用预编译语句(如 prepare() + bindParam())将 SQL 代码与数据完全分离,从根本上杜绝注入。 |
Impossible 级别(PDO prepared statement) |
| 输入验证 | 对用户输入进行严格的类型、格式、范围验证,确保输入符合预期。 | Impossible 级别(is_numeric() + intval()) |
| 最小权限原则 | 数据库连接账户应仅具有完成业务所需的最小权限,限制攻击影响范围。 | 架构级最佳实践(DVWA 未演示) |
| 错误处理 | 不向用户输出详细的数据库错误信息,防止信息泄露辅助攻击。 | HIGH 和 Impossible 级别(通用提示或无输出) |
| CSRF 防护 | 使用一次性令牌验证请求合法性,防止跨站请求伪造攻击。 | Impossible 级别(checkToken()) |
七、总结
1、漏洞全景回顾
下表从四个关键防御层级(输入层、查询层、输出层、限制层)对比了 DVWA SQL 注入四个安全级别的防护差异:
| 防御层级 | LOW 级别 | MEDIUM 级别 | HIGH 级别 | Impossible 级别 |
|---|---|---|---|---|
| 输入层 | 🔴 无任何过滤 | 🟠 使用转义函数(mysqli_real_escape_string()) |
🟠 无过滤(依赖 SESSION 存储) | 🟢 CSRF Token + 类型验证(is_numeric())+ 类型转换(intval()) |
| 查询层 | 🔴 直接拼接 SQL 语句 | 🔴 直接拼接 SQL 语句 | 🔴 直接拼接 SQL 语句 | 🟢 参数化查询(PDO 预编译语句) |
| 输出层 | 🔴 直接回显详细数据库错误信息 | 🔴 直接回显详细数据库错误信息 | 🟡 仅返回通用错误提示("Something went wrong.") | 🟢 不向用户输出任何数据库错误信息 |
| 限制层 | 🔴 无任何结果限制 | 🔴 无任何结果限制 | 🟠 使用 LIMIT 1(可被注释符绕过) |
🟢 LIMIT 1 + rowCount() == 1 双重限制 |
2、关键启发
通过分析 DVWA SQL 注入四个级别的攻防演进,我们可以得出以下关键安全启示:
- 转义函数并非万能 :MEDIUM 级别的案例表明,
mysqli_real_escape_string()只能防御字符型注入,对数字型注入完全无效。依赖单一转义函数无法提供全面的 SQL 注入防护。 - 前端限制不可信:MEDIUM 级别的前端下拉菜单限制可被 Burp Suite 等工具轻松绕过。任何仅在前端实施的控制都只是用户体验优化,而非安全屏障。
LIMIT限制可被绕过 :HIGH 级别的LIMIT 1子句可通过 SQL 注释符(如#)轻松注释掉,无法阻止攻击者通过UNION SELECT提取大量数据。- 参数化查询是根本解决方案:Impossible 级别通过 PDO 参数化查询(预编译语句)实现了 SQL 代码与数据的完全分离,从根本上杜绝了 SQL 注入的可能性。结合 CSRF Token 验证和严格的输入验证,构建了纵深防御体系。
八、AI增强防御建议
1、智能 SQL 注入检测
传统的 SQL 注入检测方案主要依赖基于关键词的黑名单和人工维护的规则,这种方法在面对新型或经过混淆的注入攻击时,容易出现误报和漏报。AI 技术可以从以下维度增强检测能力:
| 传统方案 | AI 增强方案 |
|---|---|
| 基于关键词的黑名单 | 基于语义的深度分析 |
| 依赖人工维护规则 | 自动学习正常查询模式 |
| 容易被混淆绕过 | 识别编码、注释等混淆手法 |
实现思路:
- 建立查询模式基线:收集正常业务场景下的 SQL 查询语句,构建查询模式基线,作为判断正常与异常行为的基础。
- 意图识别与分析:利用自然语言处理(NLP)模型分析用户输入的真实意图,区分正常的业务查询与潜在的恶意注入意图。
- 异常模式识别与阻断:通过机器学习模型识别偏离正常查询模式的异常请求,并实时进行阻断或告警。
2、基于查询结构的异常检测
AI 可以通过分析 SQL 查询的结构特征,更精准地识别潜在的注入攻击。下表对比了正常查询与 SQL 注入攻击在多个维度上的差异:
| 特征维度 | 正常查询 | SQL 注入攻击 |
|---|---|---|
| 查询复杂度 | 简单(通常为单表查询) | 复杂(常包含多表 UNION、子查询等) |
| 关键字组合 | 符合业务逻辑的关键字 | 包含 UNION、INFORMATION_SCHEMA、SELECT 等敏感关键字 |
| 注释符使用 | 无或极少 | 频繁使用 #、--、/* */ 等 SQL 注释符 |
| 查询结果量 | 符合业务预期的数量 | 异常增多(如一次性提取所有用户数据)或减少 |
3、实施优先级建议
在实际部署 AI 增强的 SQL 注入防御措施时,建议遵循以下优先级,以平衡实施成本与防御效果:
| 优先级 | 措施 | 实施难度 | 防御效果 |
|---|---|---|---|
| 高 | 参数化查询(预编译语句) | 低 | 高 |
| 高 | 输入类型验证 | 低 | 高 |
| 高 | CSRF Token 防护 | 低 | 高 |
| 中 | 智能 SQL 注入检测(基于规则+AI) | 中 | 中 |
| 低 | AI 语义分析与异常检测 | 高 | 高 |
说明:
- 高优先级措施是防御 SQL 注入的基石,应优先实施。它们技术成熟、成本低,且能从根本上消除大部分注入风险。
- 中优先级措施可作为传统 WAF 的增强,在已有基础防护上增加智能检测层,提升对新型和变种攻击的识别能力。
- 低优先级措施代表了更前沿的防御思路,虽然实施难度和成本较高,但能提供更深层次的语义理解和自适应防御能力。
4、总结
AI 时代的 SQL 注入防御,其核心并非用 AI 完全替代传统防御手段,而是利用 AI 技术对传统防御体系进行智能化增强。
- 传统防御:提供基础的安全基线,如参数化查询、CSRF Token 验证、严格的输入验证等。这些是必须落实的"基本功",能有效抵御绝大多数已知攻击模式。
- AI 增强:提供智能化的威胁识别、异常行为检测和自适应响应能力。AI 能够学习正常的业务模式,识别偏离基线的异常请求,并对经过混淆、编码的新型攻击手法保持较高的检测率。
只有将扎实的传统防御与灵活的 AI 增强相结合,才能构建起真正健壮(robust)、自适应且可持续演进的 SQL 注入防御体系。
免责声明:本文所述内容仅供安全研究与学习交流使用,所有测试均在本地授权靶场(DVWA)环境中进行。未经授权,严禁将文中技术用于任何非法目的。
📌 本文是专栏「DVWA通关全记录:从漏洞复现到安全防御」的第 08 篇文章。
- 上一篇 :从逻辑漏洞到顺序验证:DVWA 不安全验证码模块完整漏洞分析教程
- 下一篇:将进入 SQL Injection (Blind)(SQL盲注) 模块,带你完整理解盲注漏洞的攻防全貌。
👉 点击订阅专栏,第一时间收到更新通知!
如果你在阅读过程中有任何疑问,欢迎在评论区留言交流。😊