关于 MySQL 密码你应该知道的那些事
本文将介绍MySQL用户密码相关的一些知识,以及5.6中对于安全性的一些改进
MySQL用户密码是如何生成和保存的
如果你已经接触MySQL一段时间了,那么想必你一定知道MySQL把所有用户的用户名和密码的密文存放在mysql.user
表中。大致的形式如下:
<span class="pln">mysql </span><span class="pun">[</span><span class="pln">localhost</span><span class="pun">]</span><span class="pun">{</span><span class="pln">msandbox</span><span class="pun">}</span><span class="pun">(</span><span class="pln">mysql</span><span class="pun">)</span><span class="pun">></span><span class="kwd">select</span><span class="pln"> user</span><span class="pun">,</span><span class="pln">password </span><span class="kwd">from</span><span class="pln"> mysql</span><span class="pun">.</span><span class="pln">user</span><span class="pun">;</span>
<span class="pun">+----------------+-------------------------------------------+</span>
<span class="pun">|</span><span class="pln"> user </span><span class="pun">|</span><span class="pln"> password </span><span class="pun">|</span>
<span class="pun">+----------------+-------------------------------------------+</span>
<span class="pun">|</span><span class="pln"> root </span><span class="pun">|</span><span class="pun">*</span><span class="lit">6C387FC3893DBA1E3BA155E74754DA6682D04747</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> plain_password </span><span class="pun">|</span><span class="pun">*</span><span class="lit">861D75A7F79DE84B116074893BBBA7C4F19C14FA</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> msandbox </span><span class="pun">|</span><span class="pun">*</span><span class="lit">6C387FC3893DBA1E3BA155E74754DA6682D04747</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> msandbox </span><span class="pun">|</span><span class="pun">*</span><span class="lit">6C387FC3893DBA1E3BA155E74754DA6682D04747</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> msandbox_rw </span><span class="pun">|</span><span class="pun">*</span><span class="lit">6C387FC3893DBA1E3BA155E74754DA6682D04747</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> msandbox_rw </span><span class="pun">|</span><span class="pun">*</span><span class="lit">6C387FC3893DBA1E3BA155E74754DA6682D04747</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> msandbox_ro </span><span class="pun">|</span><span class="pun">*</span><span class="lit">6C387FC3893DBA1E3BA155E74754DA6682D04747</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> msandbox_ro </span><span class="pun">|</span><span class="pun">*</span><span class="lit">6C387FC3893DBA1E3BA155E74754DA6682D04747</span><span class="pun">|</span>
<span class="pun">|</span><span class="pln"> rsandbox </span><span class="pun">|</span><span class="pun">*</span><span class="pln">B07EB15A2E7BD9620DAE47B194D5B9DBA14377AD </span><span class="pun">|</span>
<span class="pun">+----------------+-------------------------------------------+</span>
<span class="lit">9</span><span class="pln"> rows </span><span class="kwd">in</span><span class="kwd">set</span><span class="pun">(</span><span class="lit">0.01</span><span class="pln"> sec</span><span class="pun">)*</span>
可见MySQL在其内部是不存放用户的明文密码的(这个也是一般程序对于敏感信息的最基础保护)。一般来说密文是通过不可逆加密算法得到的。这样即使敏感信息泄漏,除了暴力破解是无法快速从密文直接得到明文的。
MySQL用的是哪种不可逆算法来加密用户密码的
MySQL实际上是使用了两次SHA1夹杂一次unhex的方式对用户密码进行了加密。具体的算法可以用公式表示:password_str = concat('*', sha1(unhex(sha1(password))))
我们可以用下面的方法做个简单的验证。
<span class="pln">mysql </span><span class="pun">[</span><span class="pln">localhost</span><span class="pun">]</span><span class="pun">{</span><span class="pln">msandbox</span><span class="pun">}</span><span class="pun">(</span><span class="pln">mysql</span><span class="pun">)</span><span class="pun">></span><span class="kwd">select</span><span class="pln"> password</span><span class="pun">(</span><span class="str">'mypassword'</span><span class="pun">),</span><span class="pln">concat</span><span class="pun">(</span><span class="str">'*'</span><span class="pun">,</span><span class="pln">sha1</span><span class="pun">(</span><span class="pln">unhex</span><span class="pun">(</span><span class="pln">sha1</span><span class="pun">(</span><span class="str">'mypassword'</span><span class="pun">))));</span>
<span class="pun">+-------------------------------------------+---------------------------------------------+</span>
<span class="pun">|</span><span class="pln"> password</span><span class="pun">(</span><span class="str">'mypassword'</span><span class="pun">)</span><span class="pun">|</span><span class="pln"> concat</span><span class="pun">(</span><span class="str">'*'</span><span class="pun">,</span><span class="pln">sha1</span><span class="pun">(</span><span class="pln">unhex</span><span class="pun">(</span><span class="pln">sha1</span><span class="pun">(</span><span class="str">'mypassword'</span><span class="pun">))))</span><span class="pun">|</span>
<span class="pun">+-------------------------------------------+---------------------------------------------+</span>
<span class="pun">|</span><span class="pun">*</span><span class="pln">FABE5482D5AADF36D028AC443D117BE1180B9725 </span><span class="pun">|</span><span class="pun">*</span><span class="pln">fabe5482d5aadf36d028ac443d117be1180b9725 </span><span class="pun">|</span>
<span class="pun">+-------------------------------------------+---------------------------------------------+</span>
<span class="lit">1</span><span class="pln"> row </span><span class="kwd">in</span><span class="kwd">set</span><span class="pun">(</span><span class="lit">0.01</span><span class="pln"> sec</span><span class="pun">)</span>
MySQL用户密码的不安全性
其实MySQL在5.6版本以前,对于对于安全性的重视度非常低,对于用户密码也不例外。例如,MySQL对于binary log中和用户密码相关的操作是不加密的。如果你向MySQL发送了例如create user
,grant user ... identified by
这样的携带初始明文密码的指令,那么会在binary log中原原本本的被还原出来。我们通过下面的例子来验证。
创建一个用户:
<span class="pln">mysql </span><span class="pun">[</span><span class="pln">localhost</span><span class="pun">]</span><span class="pun">{</span><span class="pln">msandbox</span><span class="pun">}</span><span class="pun">(</span><span class="pln">mysql</span><span class="pun">)</span><span class="pun">></span><span class="pln"> create user plain_password identified </span><span class="kwd">by</span><span class="str">'plain_pass'</span><span class="pun">;</span>
<span class="typ">Query</span><span class="pln"> OK</span><span class="pun">,</span><span class="lit">0</span><span class="pln"> rows affected </span><span class="pun">(</span><span class="lit">0.00</span><span class="pln"> sec</span><span class="pun">)</span>
用mysqlbinlog查看二进制日志:
<span class="pln">shell</span><span class="pun">></span><span class="pln"> mysqlbinlog binlog</span><span class="pun">.</span><span class="lit">000001</span>
<span class="com"># at 106</span>
<span class="com">#150227 23:37:59 server id 1 end_log_pos 223 Query thread_id=1 exec_time=0 error_code=0</span>
<span class="kwd">use</span><span class="pln"> mysql</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">SET TIMESTAMP</span><span class="pun">=</span><span class="lit">1425051479</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">SET </span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">pseudo_thread_id</span><span class="pun">=</span><span class="lit">1</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">SET </span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">foreign_key_checks</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">sql_auto_is_null</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">unique_checks</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">autocommit</span><span class="pun">=</span><span class="lit">1</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">SET </span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">sql_mode</span><span class="pun">=</span><span class="lit">0</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">SET </span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">auto_increment_increment</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">auto_increment_offset</span><span class="pun">=</span><span class="lit">1</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="com">/*!\C latin1 *//*!*/</span><span class="pun">;</span>
<span class="pln">SET </span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">character_set_client</span><span class="pun">=</span><span class="lit">8</span><span class="pun">,@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">collation_connection</span><span class="pun">=</span><span class="lit">8</span><span class="pun">,@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">collation_server</span><span class="pun">=</span><span class="lit">8</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">SET </span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">lc_time_names</span><span class="pun">=</span><span class="lit">0</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">SET </span><span class="pun">@</span><span class="lit">@session</span><span class="pun">.</span><span class="pln">collation_database</span><span class="pun">=</span><span class="pln">DEFAULT</span><span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">create user plain_password identified </span><span class="kwd">by</span><span class="str">'plain_pass'</span>
<span class="com">/*!*/</span><span class="pun">;</span>
<span class="pln">DELIMITER </span><span class="pun">;</span>
<span class="com"># End of log file</span>
<span class="pln">ROLLBACK </span><span class="com">/* added by mysqlbinlog */</span><span class="pun">;</span>
<span class="com">/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/</span><span class="pun">;</span>
MySQL5.6中对于用户密码的安全性加强
好在MySQL5.6开始对安全性有了一定的重视,为了杜绝明文密码出现在binlog中的情况,MySQL引入了一系列会以密文方式记录二进制日志的命令:
- 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提示,具体如下:
<span class="pln">slave1 </span><span class="pun">[</span><span class="pln">localhost</span><span class="pun">]</span><span class="pun">{</span><span class="pln">msandbox</span><span class="pun">}</span><span class="pun">((</span><span class="pln">none</span><span class="pun">))</span><span class="pun">></span><span class="pln"> change master to master_host</span><span class="pun">=</span><span class="str">'127.0.0.1'</span><span class="pun">,</span><span class="pln">master_port </span><span class="pun">=</span><span class="lit">21288</span><span class="pun">,</span><span class="pln">master_user</span><span class="pun">=</span><span class="str">'rsandbox'</span><span class="pun">,</span><span class="pln">master_password</span><span class="pun">=</span><span class="str">'rsandbox'</span><span class="pun">,</span><span class="pln">master_auto_position</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span>
<span class="typ">Query</span><span class="pln"> OK</span><span class="pun">,</span><span class="lit">0</span><span class="pln"> rows affected</span><span class="pun">,</span><span class="lit">2</span><span class="pln"> warnings </span><span class="pun">(</span><span class="lit">0.04</span><span class="pln"> sec</span><span class="pun">)</span>
<span class="pln">slave1 </span><span class="pun">[</span><span class="pln">localhost</span><span class="pun">]</span><span class="pun">{</span><span class="pln">msandbox</span><span class="pun">}</span><span class="pun">((</span><span class="pln">none</span><span class="pun">))</span><span class="pun">></span><span class="pln"> show warnings</span><span class="pun">;</span>
<span class="pun">+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+</span>
<span class="pun">|</span><span class="typ">Level</span><span class="pun">|</span><span class="typ">Code</span><span class="pun">|</span><span class="typ">Message</span><span class="pun">|</span>
<span class="pun">+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+</span>
<span class="pun">|</span><span class="typ">Note</span><span class="pun">|</span><span class="lit">1759</span><span class="pun">|</span><span class="typ">Sending</span><span class="pln"> passwords </span><span class="kwd">in</span><span class="pln"> plain text without SSL</span><span class="pun">/</span><span class="pln">TLS </span><span class="kwd">is</span><span class="pln"> extremely insecure</span><span class="pun">.</span><span class="pun">|</span>
<span class="pun">|</span><span class="typ">Note</span><span class="pun">|</span><span class="lit">1760</span><span class="pun">|</span><span class="typ">Storing</span><span class="typ">MySQL</span><span class="pln"> user name </span><span class="kwd">or</span><span class="pln"> password information </span><span class="kwd">in</span><span class="pln"> the master info repository </span><span class="kwd">is</span><span class="kwd">not</span><span class="pln"> secure </span><span class="kwd">and</span><span class="kwd">is</span><span class="pln"> therefore </span><span class="kwd">not</span><span class="pln"> recommended</span><span class="pun">.</span><span class="typ">Please</span><span class="pln"> consider </span><span class="kwd">using</span><span class="pln"> the USER </span><span class="kwd">and</span><span class="pln"> PASSWORD connection options </span><span class="kwd">for</span><span class="pln"> START SLAVE</span><span class="pun">;</span><span class="pln"> see the </span><span class="str">'START SLAVE Syntax'</span><span class="kwd">in</span><span class="pln"> the </span><span class="typ">MySQL</span><span class="typ">Manual</span><span class="kwd">for</span><span class="pln"> more information</span><span class="pun">.</span><span class="pun">|</span>
<span class="pun">+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+</span>
<span class="lit">2</span><span class="pln"> rows </span><span class="kwd">in</span><span class="kwd">set</span><span class="pun">(</span><span class="lit">0.00</span><span class="pln"> sec</span><span class="pun">)</span>