MySQL 中常见的注入漏洞及修复
SQL 注入是所有数据库安全问题中出现频率最高的漏洞之一。
只要你的系统和 MySQL 交互,一旦输入过滤不到位、SQL 构造不安全,就可能被入侵。
-
• MySQL 中常见的注入方式有哪些? -
• 每种注入背后的原理是什么? -
• 如何正确修复,做到企业级防护?
非常适合做安全排查、代码 Review、面试准备。
一、什么是注入漏洞?(一句话解释)
将用户输入拼接到 SQL 中,使输入被当作 SQL 代码执行。
例:
SELECT * FROM users WHERE name = '$name';
攻击者输入:
' OR '1'='1
SQL 变成:
SELECT * FROM users WHERE name = '' OR '1'='1';
恒真 → 直接绕过登录。
二、MySQL 常见的注入漏洞类型(完整列表)
下面是业务系统中最常见的 7 类注入漏洞👇
1)数字型注入(最容易被忽略)
错误代码:
SELECT * FROM products WHERE id = $id;
攻击者输入:
1 OR 1=1
执行:
SELECT * FROM products WHERE id = 1 OR 1=1;
→ 全表泄漏。
✅ 修复方式
使用参数化:
WHERE id = ?
并验证参数是否为数字。
2)字符型注入(登录最常见)
错误代码:
SELECT * FROM users WHERE name = '$name' AND pwd = '$pwd';
攻击者:
name = admin
pwd = ' OR '1'='1
绕过验证。
✅ 修复方式
✔ 使用预编译 PreparedStatement
✔ 禁止字符串拼接
✔ 对输入做长度、字符规则校验
3)LIKE 模糊查询注入(经常被漏掉)
错误代码:
SELECT * FROM products WHERE title LIKE '%$keyword%';
攻击者输入:
%' UNION SELECT user, password FROM users --
为什么危险?
LIKE '%xxx%' 中间内容无法用引号隔离 → 极易被注入。
✅ 修复方式
✔ 参数化:LIKE CONCAT('%', ?, '%')
✔ 过滤 LIKE 特殊字符:% _
✔ 限制最大长度(防止超长 payload)
4)ORDER BY / GROUP BY 注入(少见但危害大)
错误代码:
SELECT * FROM orders ORDER BY $field;
攻击者:
field = id desc; drop table users;
→ 排序字段本应是固定字段,但开发者没做校验。
✅ 修复方式
✔ ORDER BY / GROUP BY 必须使用 白名单:
$allowed = ["id", "price", "create_time"];
$field = in_array($field, $allowed) ? $field : "id";
绝对不能做参数化,因为参数化不支持字段名。
5)UNION 注入(数据泄漏最多)
示例:
SELECT id, title FROM products WHERE id = '$id';
攻击者输入:
-1 UNION SELECT user, password FROM users
要求:
-
• 列数要一致 -
• 类型要兼容
修复方式
✔ 参数化 + 类型校验
✔ 永不让用户控制完整 SQL 结构
✔ WAF 过滤 UNION、SELECT 等关键字
6)报错注入(能直接爆数据库结构)
利用 MySQL 错误函数:
updatexml(), extractvalue(), floor(), exp()
错误 SQL:
AND updatexml(1, concat(0x7e, database(), 0x7e),1)
数据库名称直接出现在错误信息中。
修复方式
✔ 隐藏数据库报错信息(统一友好提示)
✔ 正常业务绝不能直接暴露 SQL 错误
✔ 日志记录真实错误,但不展示给用户
7)二次注入(非常隐蔽)
场景:
-
1. 恶意代码先存入数据库(未触发注入) -
2. 之后某个业务再次取出,并拼接进 SQL -
3. 第一次输入安全,第二次触发漏洞
例:
数据库存了:
1'; DROP TABLE users; --
之后在 “更新用户信息” 时拼接进 SQL → 直接被执行。
修复方式
✔ 所有从数据库读出的内容再次使用参数化
✔ 永不拼接 SQL
✔ 给字段做严格白名单校验
三、注入漏洞的终极修复策略
这里给你大厂统一标准👇
策略 1:永远使用预编译(Prepared Statement)
这是 最重要、防御力最高的方案。
例(Java):
String sql = "SELECT * FROM user WHERE name = ? AND pwd = ?";
例(PHP):
$stmt = $pdo->prepare("SELECT * FROM user WHERE name = ? AND pwd = ?");
完全隔离 SQL 结构与参数 → 注入不可能成功。
策略 2:对输入做严格校验(白名单策略)
根据场景限制:
-
• 字段名用 白名单 -
• 数字用正整数校验 -
• 字符串使用正则,例如用户名:
^[a-zA-Z0-9_]{3,20}$
不要放任用户输入各种奇怪字符。
策略 3:限制数据库权限(不要用 root)
应用连接数据库必须使用“最小权限”账号:
-
• Web 端只授予 SELECT、UPDATE、INSERT -
• 后台运维才能 DROP、ALTER -
• 分库分表隔离敏感业务
即使发生注入,也能最大限度降低损害。
策略 4:隐藏数据库错误(防止报错注入)
线上不可出现:
SQL syntax error near ...
应该使用统一错误码展示,实际错误写入日志。
策略 5:添加 WAF 防护
Web 防火墙可自动拦截:
-
• UNION SELECT -
• OR 1=1 -
• SLEEP(1) -
• /!/ 注释符 -
• Hex 编码
适合大型网站作为第二层防护。
**四、总结 **
MySQL 常见注入包括:数字型、字符型、UNION 注入、报错注入、盲注、二次注入、ORDER BY 注入等。
其根本原因是用户输入被当作 SQL 执行。修复的核心策略是:
参数化(Prepared Statement) + 输入白名单 + 最小权限 + 错误隐藏 + WAF 防护。实际开发中:
永远不要拼接 SQL,是所有 SQL 注入防护的终极方案。

