分享一个Mysql的小trick

Mysql的小trick

昨儿学到了一个Mysql的小trick,源码传送门 当然要提及一下这里用的是mysql5.7。其实很简单的一份源码,代码量很小很容易读懂。

关键点就在user.php中的isadmin函数

<?php
function isadmin($user)
{
    return sql("select ? in (select serialnumber from serialnumbers) as result",$user->serialnumber)[0]->result!=="0";
}

这里有一个小trick,官方doc中mysql IN表达式有这么一段

  • To comply with the SQL standard, IN returns NULL not only if the expression on the left hand side is NULL, but also if no match is found in the list and one of the expressions in the list is NULL.

意思就是 IN 在左边的表达式为 NULL 时返回 NULL ,而且在列表中没有找到匹配项,并且列表中的一个表达式为 NULL 时返回 NULL 。 看个例子就知道什么意思了:

mysql> select NULL in (1,2);
+---------------+
| NULL in (1,2) |
+---------------+
|          NULL |
+---------------+
1 row in set (0.00 sec)

mysql> select 3 in (null,2);
+---------------+
| 3 in (null,2) |
+---------------+
|          NULL |
+---------------+
1 row in set (0.00 sec)

mysql> select 3 in (1,NULL);
+---------------+
| 3 in (1,NULL) |
+---------------+
|          NULL |
+---------------+
1 row in set (0.00 sec)

然后在isadmin中进行判断的时候

return null!==0 //true

从而成功getflag。 也就是说我们注册的时候序列号输入nullserialnumber都可以。如果有人对第二个答案有疑问可以自己尝试就明白了。

简单的一些思考

探究原理其实这是一个错误的设计,billion-dollar mistake,简单说就是任何一个指针都应该指向某个东西,但是空指针却不是指向0。mysql的bool类型实质是tinyint(1),范围是范围是-128-127,也就是说正常情况下我们只能在bool类型的列中插入-128-127,值为0即是假,非0即是真,这就是mysql的bool逻辑。

但是它还允许了一个NULL的存在:

mysql> create table test(id boolean);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into test values(0);
Query OK, 1 row affected (0.01 sec)

mysql> insert into test values(1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(-12);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(12);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(null);
Query OK, 1 row affected (0.00 sec)

mysql> select * from test;
+------+
| id   |
+------+
|    0 |
|    1 |
|  -12 |
|   12 |
| NULL |
+------+

既然允许了NULL的存在,那它该如何参与到正常的BOOL运算中呢?

那我们研究下作为null怎么参与到bool运算中。

mysql> select null or true;
+--------------+
| null or true |
+--------------+
|            1 |
+--------------+
1 row in set (0.00 sec)

mysql> select null and false;
+----------------+
| null and false |
+----------------+
|              0 |
+----------------+
1 row in set (0.00 sec)

mysql> select null or false;
+---------------+
| null or false |
+---------------+
|          NULL |
+---------------+
1 row in set (0.00 sec)

mysql> select null and true;
+---------------+
| null and true |
+---------------+
|          NULL |
+---------------+
1 row in set (0.00 sec)

前两个是毫无疑问的,后两个发现都是null。