

SQL注入长期占据OWASPTop10漏洞榜单前列,是数据库安全领域最具破坏力的攻击手法之一。只要系统与MySQL存在交互,且未对用户输入实施严格的过滤与隔离,攻击者便有可能通过精心构造的输入劫持SQL语义,进而窃取、篡改甚至销毁数据。本文系统梳理MySQL环境下常见的注入漏洞类型,剖析其底层原理,并提供可落地的企业级防护方案,适用于安全审计、代码审查及技术面试等场景。
一、注入漏洞的本质
定义:当应用程序将用户输入直接拼接到SQL语句中,且未对输入内容进行合理转义或隔离时,攻击者可通过输入特殊构造的字符串,改变原始SQL语句的逻辑结构,使其执行非预期的数据库操作。
经典示例:
```sql
原始查询
SELECTFROMusersWHEREname='$name';
攻击者输入:'OR'1'='1
实际执行
SELECTFROMusersWHEREname=''OR'1'='1';
```
由于`'1'='1'`恒为真,该查询将返回所有用户记录,攻击者可借此绕过登录认证。
二、MySQL常见注入漏洞类型及修复方案
1.数字型注入
漏洞代码:
```java
Stringsql="SELECTFROMproductsWHEREid="+request.getParameter("id");
```
攻击向量:`1OR1=1`
执行结果:`WHEREid=1OR1=1`,导致全表数据泄漏。
修复方案:
使用参数化查询:`WHEREid=?`
对输入进行严格数字校验,如正则`^\\d+$`
2.字符型注入
漏洞代码:
```java
Stringsql="SELECTFROMusersWHEREname='"+name+"'ANDpwd='"+pwd+"'";
```
攻击向量:`pwd`输入`'OR'1'='1`
执行结果:恒真条件绕过登录校验。
修复方案:
强制使用预编译语句(PreparedStatement)
对字符串类型实施白名单校验(如长度、字符集)
3.LIKE模糊查询注入
漏洞代码:
```java
Stringsql="SELECTFROMproductsWHEREtitleLIKE'%"+keyword+"%'";
```
攻击向量:`%'UNIONSELECTuser,passwordFROMusers`
风险点:LIKE子句中的单引号无法有效隔离用户输入,易被注入。
修复方案:
参数化绑定:`WHEREtitleLIKECONCAT('%',?,'%')`
转义LIKE通配符(`%`、`_`),如将其替换为转义形式
限制输入最大长度,防止超长payload
4.ORDERBY/GROUPBY注入
漏洞代码:
```java
Stringsql="SELECTFROMordersORDERBY"+sortField;
```
攻击向量:`sortField`传入`idDESC;DROPTABLEusers`
风险点:ORDERBY后不能使用参数化占位符,必须依赖白名单校验。
修复方案:
严格白名单:只允许预定义的字段名参与排序
```java
String[]allowed={"id","price","create_time"};
sortField=Arrays.asList(allowed).contains(sortField)?sortField:"id";
```
杜绝用户直接控制字段名
5.UNION注入
漏洞代码:
```java
Stringsql="SELECTid,titleFROMproductsWHEREid='"+id+"'";
```
攻击向量:`1'UNIONSELECTuser,passwordFROMusers`
前提条件:攻击者需保证UNION前后字段数一致且类型兼容。
修复方案:
参数化查询+类型强校验
避免将用户输入直接拼接到SELECT列或FROM表的位置
部署WAF拦截UNION、SELECT等关键词
6.报错注入
原理:利用MySQL函数(如`updatexml()`、`extractvalue()`)在参数错误时返回错误信息,攻击者可通过构造参数将数据库内容带入错误提示中。
攻击示例:
```sql
ANDupdatexml(1,concat(0x7e,database(),0x7e),1)
```
数据库名称将出现在错误信息中,攻击者可借此逐步获取库结构。
修复方案:
统一错误处理:前端展示通用错误页,真实错误写入日志
关闭数据库错误回显,禁止将SQL异常堆栈暴露给用户
7.二次注入
特点:恶意输入在首次写入数据库时未触发注入(因使用了转义或参数化),但在后续查询中被拼接进SQL语句时触发。
场景示例:
用户注册时输入用户名:`admin'`
数据库转义后存储为`admin'`,未触发注入
后台修改用户密码时,拼接用户名构造SQL:
```sql
UPDATEusersSETpwd='newpwd'WHEREname='admin''
```
``将后续注释掉,可能影响多条记录。
修复方案:
所有从数据库读取的数据再次使用时,仍需参数化处理
永不信任任何来源的数据,包括数据库本身
对关键字段实施白名单校验
三、企业级防护终极策略
策略1:预编译语句(PreparedStatement)——根本性防御
参数化查询将SQL结构与参数数据完全分离,数据库仅将参数视为纯文本值处理,从根本上杜绝注入。
Java示例:
```java
Stringsql="SELECTFROMuserWHEREname=?ANDpwd=?";
PreparedStatementps=connection.prepareStatement(sql);
ps.setString(1,name);
ps.setString(2,pwd);
ResultSetrs=ps.executeQuery();
```
策略2:白名单校验——输入规范强制
字段名:使用枚举或数组限定可接受的列名
数值:确保为正整数或符合业务范围
字符串:采用正则限定字符集(如用户名仅允许字母、数字、下划线)
策略3:最小权限原则——降低损害范围
应用程序连接数据库应使用专用账号,仅授予必要的权限(如SELECT、INSERT、UPDATE)
禁止使用root或高权限账号连接应用
敏感业务(如支付、用户数据)可独立分库,使用不同凭证
策略4:错误信息隐藏——切断信息泄露渠道
生产环境禁止将SQL错误堆栈返回给客户端
统一返回友好提示,如“系统繁忙,请稍后重试”
详细错误记录至日志系统,供运维排查
策略5:WAF防护——纵深防御
Web应用防火墙可实时拦截常见注入载荷,如:
包含`UNIONSELECT`、`OR1=1`的请求
使用`SLEEP()`、`BENCHMARK()`的延时注入尝试
特殊注释符(如`/!`)、Hex编码等绕过手法
四、总结
| 注入类型 | 核心防护手段 |
| 数字型 | 参数化+数字校验 |
| 字符型 | 参数化(强制) |
| LIKE注入 | CONCAT参数化+通配符转义 |
| ORDERBY注入 | 白名单校验 |
| UNION注入 | 参数化+WAF拦截关键词 |
| 报错注入 | 隐藏数据库错误信息 |
| 二次注入 | 读取后再次参数化 |
根本原则:永不拼接SQL字符串。只要将用户输入以参数形式传递给数据库,而非直接嵌入SQL语句,即可抵御绝大多数注入攻击。在此基础上,辅以白名单校验、最小权限与WAF防护,方能构建纵深、立体的数据库安全防线。

一家致力于优质服务的软件公司
8年互联网行业经验1000+合作客户2000+上线项目60+服务地区

关注微信公众号
