mysql insert sql注入_SQL注入-双注入的学习

本文详细解释了在MySQL中使用GROUP BY与RAND()函数组合时可能出现的错误,通过实例演示了为何报错,并揭示了双查询时rand()多次触发导致的主键冲突。关键在于理解group by的虚拟表机制和count(*)的计数逻辑。
摘要由CSDN通过智能技术生成

0x1 需要用到的MySQL函数

count():用来统计表中或数组中记录的一个函数 count(*)某表中所有的列

4c195dc54b8fe5d1fc2e25f1f3e136b6.png

floor():返回小于等于该值的最大整数【向下取整】

9050b8be70cd7a0db68348933087dd08.png

rand(): 产生随机数

1f715a0569dc67feca2f4a50ae8c2b50.png

0x2原理分析

接下来我们再分析其报错的形成 原因:

先谈group by 函数:

在表中再插入两条数据,name值都为“bbb”:

mysql> INSERT INTO test VALUES("3","bbb");mysql> INSERT INTO test VALUES("4","bbb");

成功后表如下:

16da7486ef3d4ce7d4a1565cdf5b2378.png

这时候我们使用group by 语句时,MySQL会将查询结果分类汇总,重复的内容会合并为一项:

mysql> SELECT name FROM test GROUP BY name;

3eda1f46568d8ed8214064a40fb37d78.png

这时候再使用count()函数就可以对不同的条目计数:

mysql> SELECT count(*),name FROM test GROUP BY name;

如图:aaa有一条,bbb有3条

d0f76cf41ac59974eaf0ac34356f13ff.png

其背后的实现原理如下:

在执行group by name语句时,MySQL会在内部建立一个虚拟表,用来储存列的数据,表中会有一个group_key值作为表的主键,这里的主键就是用来分类的name列中获取,当查询数据时,取数据库数据,然后查看虚拟表中存在不,不存在则插入新记录

49fe4c205e70aba9e511a0b04cc38402.png

当读取到第一行数据时,aaa不存在,将aaa放入主键列中,1放在id列中

d755840580542312bc466036fd59291b.png

然后继续往下走,到了bbb,不存在,也放进去

7671b23219ae28216f86142665b438d5.png

往下执行,遇到多余的bbb,已经有bbb存在,就汇总在一起,内部情况如下:

e12cdf4c86083a7a3882049cd9b1b79c.png

如下,最后在查询的时候根据group by内部的实现方式返回分类后的结果:

935ecd2b3978086331ef8c02ab98857f.png

当我们加上count(*)函数时,操作过程为:查看虚拟表是否存在该主键值,不存在则插入新记录,存在则count(*)字段直接加1

这样就能对上面的分类结果进行统计,然后将统计结果返回:

abd5b47a4bc909d9f9f2c0e7a609cd4f.png

所以双查询报错的关键就在这里,主要的原因在于rand()函数在group by的过程中被触发了多次,

0x3 报错原理

让我们回看一下构造的报错语句:

mysql> SELECT count(*),concat((SELECT database()),"~",floor(rand()*2))as a FROM test GROUP BY a;

执行前虚拟表为空:

66c7f8fe4f69d01d8b53f848ebc12437.png

当第一次执行时,group by 分组,其取的数据的是以a为别名的这条语句,假设这时的concat((SELECT database()),"~",floor(rand()*2))生成结果为sql_test~0,group就以sql_test~0查询虚拟表,发现表中没有该值的主键,于是将这条语句的结果插入到虚拟表中。

注意!是将这条语句的结果插入到虚拟表中,而不是将 sql_test~0 插入到虚拟表中,如下:

(将concat((SELECT database()),"~",floor(rand()*2)) 以a为别名,方便作图)

58541db5d176552ca5491d81bb52d728.png

由于虚拟表没有内容,所以会将其插入到虚拟表中,这里的插入过程中,由于插入的是a语句的结果,所以在插入时a语句中的rand()函数会再次执行,即插入的值可能为sql_test~0 也可能为 sql_test~1 ,这里假设插入时a执行的结果为sql_test~0 :

6c9b627798e4acd310b4e64d567e94df.png

所以上面的情况就是用sql_test~1这个结果查询虚拟表,不存在该数据,于是插入虚拟表,插入时又运算一次,然后插入的值变成了sql_test~0,所以这就是主要的冲突,表中只有一条数据还好,即使查询虚拟表的值和插入虚拟表的值不是同一个,但虚拟表也只生成一条记录,不会出现问题。

然而当表的数据出现两条以上的时候,第group by 在处理完第一条数据后会往下继续处理第二条,于是第二条还会按第一条的处理方式进行:

3254b480548f75d8b4006f19c6f79dba.png

于是就会报错,报错内容如下:

ERROR 1062 (23000): Duplicate entry 'sql_test~0' for key 'group_key'

632845b3302621c8959c4af93bce9e60.png

如果第二次查询和插入的结果都一致:就会有下面两种情况:

都是sql_test~0:表里已存在,该主键的count(*)值+1

都是sql_test~1:表里没有,插入形成新的主键

b48b5c5203f484fdfca983ae2a5d3c60.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值