今天使用MySQL的时候遇到一个问题,建了一个登录地点任意的用户却无法在本地登录。后来才发现是受系统初始化数据时生成的匿名用户的影响。这个匿名用户的存在不但有安全风险,还影响正常使用,建议尽快删除。
问题出现的具体过程是这样的:
创建一个新用户,不限制登录地点:
CREATE USER 'xindong' IDENTIFIED BY 'xindong';
向这个用户授权,可以无限制使用数据库:
GRANT ALL ON `nhcloud`.* TO 'xindong';
然后试图用这个用户登录数据库:
mysql -u xindong -p nhcloud
但总是返回错误:
Enter password: *******
ERROR 1045 (28000): Access denied for user 'xindong'@'localhost' (using password: YES)
以为是密码输错了。但输了好几次错误依旧。
查看mysql.user表,发现了如下用户:
mysql> select host,user from mysql.user;
+-----------+---------+
| host | user |
+-----------+---------+
| % | xindong |
| 127.0.0.1 | root |
| ::1 | root |
| localhost | |
| localhost | root |
+-----------+---------+
5 rows in set (0.00 sec)
除了root用户和我刚建的用户'xindong'@'%'外,还有个匿名用户''@'localhost'。这个用户的存在允许不提供用户名就可以在本地登录。
查阅mysql的文档发现,就是这个用户影响了'xingdong'@'%'在本地的登录。当我使用xingdong在本地登录时,会被认为是这样的一个用户:
host='localhost', user='xindong'
这样一个用户不但和mysql.user表中的我刚建的'xingdong'@'%'匹配,也和那个匿名用户''@'localhost'匹配。不幸的是,mysql认为那个匿名用户和它更加匹配(因为mysql在处理时认为host比user更优先),所以认为是''@'localhost'正在登录,结果就是mysql会用''@'localhost'的信息来认证我输入的密码,就出现了前述错误。
MySQL的文档里对这种产品行为有所描述(但它并没有特别提醒用户注意这种不寻常的情况):
从里面可以看出,在确定待认证用户的身份时,mysql会对匹配用户登录host和user的所有mysql.user表项进行排序。两者都相同的排序最靠前,host相同但user不同却匹配的排序其次,user相同但host不同却匹配的排序再次,两者都不同但都匹配的排序最后。
至于匿名用户,是在数据库初始化数据目录的过程中创建的。本意是方便用户快速使用,不创建特定名称的用户就可以访问数据库。但这个设计非常业余,它的存在不但带来了安全问题,还影响了其它用户的正常访问。我认为这样的功能不该放在产品发布中。
这个问题的解决方法有二:
一是另外建一个用户'xindong'@'localhost'。这样使用xindong在本地登录时,mysql会认为是这个用户在登录,会使用正确的信息认证用户。这个方法能解决问题我不喜欢,因为是在为一个画蛇添足的功能再打一个多余的补丁。
二是彻底删除那个匿名用户,一举解决它带来的安全问题和对正常使用的影响问题。使用如下SQL即可删除匿名用户:
DELETE FROM mysql.user WHERE User='';
FLUSH PRIVILEGES;