MySQL用户密码是如何生成和保存的?
如果你已经接触MySQL一段时间了,那么想必你一定知道MySQL把所有用户的用户名和密码的密文存放在mysql.user
表中。大致的形式如下:
1 2 3 4 5 6 7 8 9 |
mysql> select user,password from mysql.user; +----------------+-------------------------------------------+ | user | password | +----------------+-------------------------------------------+ | root | *6C387FC3893DBA1E3BA155E74754DA6682D04747 | | plain_password | *861D75A7F79DE84B116074893BBBA7C4F19C14FA | | msandbox | *6C387FC3893DBA1E3BA155E74754DA6682D04747 | +----------------+-------------------------------------------+ 9 rows in set (0.01 sec)* |
可见MySQL在其内部是不存放用户的明文密码的(这个也是一般程序对于敏感信息的最基础保护)。一般来说密文是通过不可逆加密算法得到的。这样即使敏感信息泄漏,除了暴力破解是无法快速从密文直接得到明文的。
MySQL用的是哪种不可逆算法来加密用户密码的?
MySQL实际上是使用了两次SHA1夹杂一次unhex的方式对用户密码进行了加密。具体的算法可以用公式表示:
1 |
password_str = concat('*', sha1(unhex(sha1(password)))) |
我们可以用下面的方法做个简单的验证。
1 2 3 4 5 6 7 |
mysql> select password('mypassword'),concat('*',sha1(unhex(sha1('mypassword')))); +-------------------------------------------+---------------------------------------------+ | password('mypassword') | concat('*',sha1(unhex(sha1('mypassword')))) | +-------------------------------------------+---------------------------------------------+ | *FABE5482D5AADF36D028AC443D117BE1180B9725 | *fabe5482d5aadf36d028ac443d117be1180b9725 | +-------------------------------------------+---------------------------------------------+ 1 row in set (0.01 sec) |
MySQL用户密码的不安全性?
其实MySQL在5.6版本以前,对于对于安全性的重视度非常低,对于用户密码也不例外。例如,MySQL对于binary log中和用户密码相关的操作是不加密的。如果你向MySQL发送了例如create user
,grant user ... identified by
这样的携带初始明文密码的指令,那么会在binary log中原本的被还原出来。我们通过下面的例子来验证。
创建一个用户:
1 2 |
mysql> create user plain_password identified by 'plain_pass'; Query OK, 0 rows affected (0.00 sec) |
用mysqlbinlog查看二进制日志:
1 2 3 4 5 6 7 |
SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; create user plain_password identified by 'plain_pass' /*!*/; DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; |
MySQL5.6于用户密码的安全性加强
好在MySQL5.6开始对安全性有了一定的重视,为了杜绝明文密码出现在binlog中的情况,MySQL引入了一系列会以密文方式记录二进制日志的命令:
1 2 3 4 5 6 |
$ CREATE USER … IDENTIFIED BY … $ GRANT … IDENTIFIED BY … $ SET PASSWORD … $ SLAVE START … PASSWORD = … (as of 5.6.4) $ CREATE SERVER … OPTIONS(… PASSWORD …) (as of 5.6.9) $ ALTER SERVER … OPTIONS(… PASSWORD …) (as of 5.6.9) |
细心你的也许会发现,change master to master_password=''
命令不在这个范畴中。这也就意味着MySQL5.6中仍然使用这样的语法来启动replication时有安全风险的。这也就是为什么5.6中使用带有明文密码的change master to
时会有warning提示,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
slave > change master to master_host='127.0.0.1',master_port=3306,master_user='rsandbox',master_password='rsandbox',master_auto_position=1; Query OK, 0 rows affected, 2 warnings (0.04 sec) slave > show warnings; +-------+------+---------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------------------------------------------------------------------------------------+ | Note | 1759 | Sending passwords in plain text without SSL/TLS is extremely insecure. | | Note | 1760 | Storing MySQL user name or password information in the master info repository is not secure and is therefore not | | | recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE | | | Syntax' in the MySQL Manual for more information. | +-------+------+---------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec) |
MySQL5.6使用validate password插件加强密码强度
MySQL5.6对密码的强度进行了加强,推出了validate_password插件,支持密码的强度要求。
安装办法,装载plugin即可:
1 |
mysql> INSTALL PLUGIN validate_password SONAME 'validate_password.so'; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
mysql> show plugins; +----------------------------+----------+--------------------+----------------------+---------+ | Name | Status | Type | Library | License | +----------------------------+----------+--------------------+----------------------+---------+ | binlog | ACTIVE | STORAGE ENGINE | NULL | GPL | | mysql_native_password | ACTIVE | AUTHENTICATION | NULL | GPL | | sha256_password | ACTIVE | AUTHENTICATION | NULL | GPL | .......................... | INNODB_SYS_VIRTUAL | ACTIVE | INFORMATION SCHEMA | NULL | GPL | | MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL | | PERFORMANCE_SCHEMA | ACTIVE | STORAGE ENGINE | NULL | GPL | | CSV | ACTIVE | STORAGE ENGINE | NULL | GPL | | FEDERATED | DISABLED | STORAGE ENGINE | NULL | GPL | | BLACKHOLE | ACTIVE | STORAGE ENGINE | NULL | GPL | | partition | ACTIVE | STORAGE ENGINE | NULL | GPL | | ARCHIVE | ACTIVE | STORAGE ENGINE | NULL | GPL | | ngram | ACTIVE | FTPARSER | NULL | GPL | | validate_password | ACTIVE | VALIDATE PASSWORD | validate_password.so | GPL | +----------------------------+----------+--------------------+----------------------+---------+ 45 rows in set (0.00 sec) |
为阻止该插件在重启时被删除可在配置文件中添加:
1 2 3 4 |
[mysqld] plugin-load=validate_password.so validate-password=FORCE_PLUS_PERMANENT validate_password_policy=2 |
当建立用户密码时,如果不符合预设的规则,那么就不会通过:
1 2 |
mysql> grant all on *.* to tester@'localhost' identified by 'tasssss'; ERROR 1819 (HY000): Your password does not satisfy the current policy requirements; |
下面来看看validate_password插件提供的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> show variables like '%validate%'; +--------------------------------------+--------+ | Variable_name | Value | +--------------------------------------+--------+ | query_cache_wlock_invalidate | OFF | | validate_password_dictionary_file | | | validate_password_length | 8 | | validate_password_mixed_case_count | 1 | | validate_password_number_count | 1 | | validate_password_policy | MEDIUM | | validate_password_special_char_count | 1 | +--------------------------------------+--------+ 7 rows in set (0.00 sec) |
validate-password=ON/OFF/FORCE/FORCE_PLUS_PERMANENT: 决定是否使用该插件(及强制/永久强制使用)。
validate_password_dictionary_file:插件用于验证密码强度的字典文件路径。
validate_password_length:密码最小长度,默认为8,最小为4。
validate_password_mixed_case_count:密码至少要包含的小写字母个数和大写字母个数。
validate_password_number_count:密码至少要包含的数字个数。
validate_password_policy:密码强度检查等级,0/LOW(只检查长度)、1/MEDIUM(检查长度、数字、大小写、特殊字符)、2/STRONG(检查长度、数字、大小写、特殊字符字典文件)。
validate_password_special_char_count:密码至少要包含的特殊字符数。
如果修改了validate_password_number_count,validate_password_special_char_count,validate_password_mixed_case_count中任何一个值,则validate_password_length将进行动态修改。另外如果你显性指定validate_password_length的值小于4,尽管不会报错,但validate_password_length的值将设为4。
从MySQL5.7开始,此插件是默认安装的。