MySQL的权限排序问题
实验:因权限表具有通配符,对权限进行排序后,匹配时引来的问题。
授权表中的通配符
- user表中User为空值表示匹配任意user name,也表示匿名用户。
- user表中Host部分用户可以使用通配符”%”和”_”在host name或者IP地址中,这些具有和模式匹配LIKE相同的意义。
- db表中,Host、User与user表中表示方法一致,同时Db列也可以具有通配符。
- tables_priv、columns_priv和procs_priv表中,只有Host列可以具有通配符。
账户登录的匹配:
由于具有通配符,这就会发生一个用户连接server可以匹配多个账户的事情发生。例如创建了账户’’@localhost和’yz’@’%’,当用户’yz’@localhost登录时两个账户都可以匹配。这样就面临着选择哪一个账户作为登录账户的问题。MySQL为解决这个问题,对user表中的记录做了排序。排序方法如下:
先按最具体的(most-specific)Host值排序。原义的host_name和IP地址算是最具体的(IP地址的具体级别specificity是不受子网掩码影响的,192.168.1.13和192.168.1.0/255.255.255.0被认为是同一具体级别)。匹配符’%’算是最不具体的级别。空字符’’也表明任意host,但排在’%’后面。有相同Host值的行首先以最具体的User值排序(空User值意味着“任何用户”并且是最不具体的)。对于具有相同具体级别的Host和User值,排序是不确定。
采用上述方法,当’yz’@localhost登录时,匹配的就是账号’’@localhost。如果用用账户’yz’@’%’的密码试图登录,可能会被告知Access denied for user 'yz'@'localhost',因为MySQL选取的登录账户是’’@localhost。
不过MySQL也比较奇怪,登录后使用select user()发现
| user() |
| yz@localhost |
使用show grants;可以看到下面结果,账户是’’@localhost。
| Grants for @localhost |
| GRANT USAGE ON *.* TO ''@'localhost' |
| GRANT CREATE ON `privtest`.* TO ''@'localhost' |
问题1,同一账户不同db_name授权信息引发的问题:
账户行为还好,不会引起太大的坑。db中的排序不注意可能就会导致MySQL结果不一致,用户权限可能‘丢失’,导致用户无法正常工作,服务故障。
db表在Host、Db和User范围列上排序,也是按照具体到不具体排序,而且匹配到第一条符合的记录后,就返回。首先其排序是不稳定排序,这会导致插入一条不相关的记录,导致其他记录排序变化,权限表现变化,部分原有权限‘丢失’。即使是稳定排序,也会导致逻辑上应该有的权限没有,例如
grant select on aaa.* to yz@localhost;
grant create on `aa%` to yz@localhost;
grant insert on `aaa%` to yz@localhost;
MySQL在做权限检查时,只会返回匹配到的第一条记录,即用户yz@localhost对aaa数据库只有select权限,不会有create和insert权限。从这个角度讲即使排序稳定,插入新的权限信息也可能会导致之前的权限‘丢失’。
相关知识:
1、mysql的权限验证有global,db,table,column多个纬度。
2、在global和db纬度中,mysql按内部权重,从大到小读取所有授权,获取第一个匹配(权重最大的)的作为内部授权验证。
3、db纬度的权重计算方法如下:
3.1 分别按照hostname,dbname,username计算。
3.2 单个字符串若不存在通配符权重为0x80,若存在通配符,则权重为第一个通配符出现的位置,最大为0x7F
3.3 举例 GRANT CREATE ON `tt_`.* TO 'tt'@‘%’ 这条授权的权重为0x010380,其中’%’为0x01 ’tt_’为0x03 ’tt’为0x80
4、当前mysql内部大部分排序采用快速排序,这是一种不稳定的排序算法。
问题2 不同账户之间授权信息引发的问题:
实验:MySQL在db层验证权限时,是否会对db中所有账户权限信息都做逐一匹配。
结论:从效果上MySQL会逐一匹配db表中的信息,遇到能匹配的就返回,导致拥护show grants的权限信息可能与实际权限不一致。(table层也是这样)
MySQL排序先后为Host、Db、User,前面user表在匹配时会发生匹配多条记录,那么在db层匹配时,同时可匹配的账户是否也会在db层发生匹配。实验:
同时创建
’yz’@’%’和’yz’@localhost账号。
对’yz’@’%’赋予权限:
grant create on yzdb to 'yz'@'%';
登录yz@localhost,执行下面语句,可以看到权限信息仅Usage。这个时候试着创建database yzdb,会发现其具有创建yzdb的权限。
mysql> show grants;
| Grants for yz@localhost |
| GRANT USAGE ON *.* TO 'yz'@'localhost' |
mysql> create database yzdb;
Query OK, 1 row affected (0.02 sec)
当撤销'yz'@'%'的创建yzdb权限,'yz'@'localhost'也会失去该权限。
在测试中,’’@localhost账户不会拥有'yz'@'%'的上述权限。在这里匹配时,’yz’不等同于’’。
上述情况也会产生权限‘丢失’问题,show grants中有的权限可能因为其他账户授权信息引发‘丢失’。