MYSQL隐式类型转换
前言
今天看了下团队发的XCTF高校战疫的WP,这里先喊一句。杨大树师傅太强了orz 几乎没帮啥忙,简单的题目都被师傅们秒了,我进度太慢,跟不上,后面难的题目基本动不了。再喊一遍,星盟的师傅tql。再接再厉,希望下次能上师傅们,帮到师傅们解题。
回顾主题,这里是从看到比赛里的sqlcheckin这道题目的WP后,才觉得很奇怪,然后百度发现并学习到了mysql的隐式类型转换的点。(比较菜没有仔细学习过mysql)
MYSQL弱类型转换
这里本地搭建一下环境(用的老的语句),php7比较高的环境用新的语句
<?php $conn = mysql_connect('127.0.0.1','root','root'); //这里使用@符号屏蔽出错信息 mysql_select_db('test',$conn); $sql ="select * from people where username = '".$_POST["username"]."' and password = '".$_POST["password"]."'"; $result = mysql_query($sql); while($row = mysql_fetch_array($result)){ echo "用户ID:" . $row['id'] . "<br/>"; echo "用户名:" . $row['username'] . "<br/>"; echo "用户密码:" . $row['password'] . "<br/>"; } mysql_close($conn); echo "<hr>"; echo "您当前执行的sql语句为:" ; echo $sql; ?>
比如很简单的一句SQL语句
select * from people where username=' 'and password=' ';
当我们在username输入'- password传入'
结果输出了一个表的内容,为什么会这样呢。是因为mysql存在的隐式类型转换的问题。
什么是隐式类型转换
在MySQL中,当操作符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容。则会发生转换隐式
也就是说,MySQL会根据需要自动将数字转换为字符串,将字符串转换数字。
这里因为存在-号,隐式类型转化将' '和' and password = '字符串进行了算术运算,转为成数字类型。
这里再举两个例子
案例一:字符串转换为数字
mysql > SELECT 1+'1';
结果:
mysql > 2
案例二: 数字转换为字符串
mysql -> SELECT CONCAT(1024,' andyqian');
结果:
'1024,' andyqian';
而通过cast()函数测试后发现字符串转为算数值时会将字符串开头的一串数字作为其转换后的数值,比如
’02admin’ ==>2 ‘admin’==>0 ’33admin’==>33
这个与php的弱类型问题相似。
所以上面这样的输入,其实就会转化成0-0=0
而username的弱类型转换值如果为0的话,那么所有的用户都将被检索到
这检索的时候,将字符串类型转化为数字,admin,yunying都被强制转化成数字类型,即为0
这里我们插入新的一行
从这里就可以看到MYSQL隐式类型转换带的问题。
所以sqlcheckin里,username必须为admin,但是password可以输出a'&'1
这里'a'&'1'执行了位运算
所以这里password为0,类型转化后,就可以检索到username='admin',字符串开头的password的数据行
若password是数字不为0,且是非0开头的字符串的话,就检索不到,这样的转换就是隐式类型转换的表现
如何避免隐式类型转换
只有当清楚的知道隐式类型转换的规则,才能从根本上避免产生隐式类型转换。MySQL也在官网描述了进行隐式类型转换的一些规则如下:
1. 隐式类型转换规则:
-
如果一个或两个参数都是NULL,比较的结果是NULL,除了NULL安全的<=>相等比较运算符。对于NULL <=> NULL,结果为true。不需要转换
-
如果比较操作中的两个参数都是字符串,则将它们作为字符串进行比较。
-
如果两个参数都是整数,则将它们作为整数进行比较。
-
如果不与数字进行比较,则将十六进制值视为二进制字符串
-
如果其中一个参数是十进制值,则比较取决于另一个参数。 如果另一个参数是十进制或整数值,则将参数与十进制值进行比较,如果另一个参数是浮点值,则将参数与浮点值进行比较
-
如果其中一个参数是TIMESTAMP或DATETIME列,另一个参数是常量,则在执行比较之前将常量转换为时间戳。
-
在所有其他情况下,参数都是作为浮点数(实数)比较的。
2.使用CAST函数显示转换
select * from people where username=cast(0 as char);
3. 类型一致
这里说的类型一致,指的是在写SQL时,参数类型一定要与数据库中的类型一致,避免产生隐式类型转换,就如刚才在文首时,如果多检查,写的SQL的参数类型与数据库中字段类型一致,也就不会不走索引了,你说是不是?
参考文章: