第5章:数据库管理 / 5.7. MySQL访问权限系统 / 5.7.9. MySQL 4.1中的密码哈希处理
5.7.9.1. 更改应用程序密码哈希值的含义

MySQL用户账户列于mysql数据库中的user表内。每个MySQL账户指定一个密码,尽管保存在userPassword列的密码不是明文,但哈希值是从表中的记录计算的。用PASSWORD()函数来计算密码的哈希值。

MySQL在客户端/服务器通信的两个阶段使用密码:

·         如果客户端试图连接服务器,有一个初始鉴定步骤,客户必须提供一个密码,并且必须与客户想要使用的账户在user表保存的哈希值匹配。

·         客户端连接后,它可以(如果有充分的权限) 设置或更改user表内所列的账户的密码哈希值值。客户端可以通过PASSWORD()函数来生成密码哈希值,或使用GRANTSET PASSWORD语句。

换句话说,当客户端首次试图连接时,服务器使用哈希值进行鉴定。如果连接的客户端调用PASSWORD()函数或使用GRANTSET语句来设置或更改密码,则服务器产生哈希值。

MySQL 4.1中密码哈希算法已经更新,提供了更好的安全性并降低了密码被截取的风险。但是,该新机制只能在MySQL 4.1(和更新版本的)服务器和客户端中使用,会产生一些兼容性问题。4.1或新客户端可以连接4.1之前的服务器,因为客户端可以同时理解旧的和新的密码哈希机制。但是,4.1之前的客户端试图连接4.1版或更新版的服务器时会遇到困难。例如,3.23mysql客户端试图连接5.1服务器时会失败并出现下面的错误消息:

shell> mysql -h localhost -u root
Client does not support authentication protocol requested
by server; consider upgrading MySQL client

出现该问题的另一个普通例子是在升级到MySQL 4.1或更新版后,试图使用旧版本的PHP mysql扩展名。(参见25.3.1节,“使用MySQL和PHP的常见问题”

下面讨论了新、旧密码机制之间的差别,以及如果你升级了服务器但需要为4.1版以前的客户端保持向后兼容性该怎样做。A.2.3节,“客户端不支持鉴定协议”中有更详细的信息。该信息将MySQL数据库从4.0版本或更低版升级到4.1版或更高版的PHP编程人员特别重要。

释:该讨论对比了4.1版的行为和4.1前的行为,这儿描述的4.1中的行为实际上从4.1.1开始。MySQL 4.1.0是一个“旧”的发布,因为它的实施机制与4.1.1版和更新版中的稍有不同。在MySQL 5.0 参考手册中详细描述了4.1.0和最新版之间的差别。

MySQL 4.1之前,PASSWORD()函数计算的密码哈希值有16个字节长。应为:

mysql> SELECT PASSWORD('mypass');
+--------------------+
| PASSWORD('mypass') |
+--------------------+
| 6f8c114b58f2ce9e   |
+--------------------+

MySQL 4.1之前,user表的Password(保存了哈希值)也是16字节长。

MySQL 4.1,已经对PASSWORD()函数进行了修改,可以生成41字节的哈希值:

mysql> SELECT PASSWORD('mypass');
+-------------------------------------------+
| PASSWORD('mypass')                        |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+

同样,user表的Password列必须有41字节长来保存这些值:

·         如果你新安装MySQL 5.1, Password列自动为41字节长。

·         MySQL 4.1(4.1.14.1系列的更新版)升级到MySQL 5.1,应不会出现相关问题,因为两个版本使用相同的密码哈希机制。如果你想要将更早版本的MySQL升级到MySQL5.1,你应先升级到4.1,然后将4.1升级到5.1

加宽的Password列可以同时保存新、旧格式的密码哈希值。可以有两种方式确定任何给定格式的密码哈希值:

·         明显的不同之处是长度(16字节和41字节)

·         2个不同之处是新格式的密码哈希值都以‘*’字符开头,而旧格式的密码绝对不是。

 长密码哈希值具有更好的加密属性,并且客户端根据长哈希值进行鉴定比旧的短哈希值更加安全。

短密码哈希值和长密码哈希值之间的不同之处与服务器如何使用密码进行鉴定以及如何为执行密码更改操作的连接的客户端生成密码哈希值都有关。

服务器使用密码哈希值进行鉴定的方式受Password列的宽度影响:

·         如果列较短,只用短哈希鉴定。

·         如果列较长,可以有短或长哈希值,并且服务器可以使用任何一种格式:

o        4.1之前的客户端可以连接,它们只可以使用旧的哈希机制,它们可以只鉴定有短哈希的账户。

o        4.1及以后版本的客户端可以鉴定有短哈希或长哈希的账户。

对于短哈希账户的鉴定过程,4.1和以后版本的客户端比为旧版本的客户端实际要安全得多。从安全性角度,从最低安全到最安全的梯度为:

·         4.1之前的客户端用短密码哈希值进行鉴定

·         4.1或以后版本的客户端用短密码哈希值进行鉴定

·         4.1或以后版本的客户端用长密码哈希值进行鉴定

服务器为连接的客户端生成密码哈希值的方式受Password列宽度和--old-passwords选项的影响。4.1或更新版本的服务器只有满足某个条件才生成长哈希:Password列必须足够宽以容纳长哈希值并且未给定--old-passwords选项。这些条件适合:

·         Password列必须足够宽以容纳长哈希(41字节)值。如果列没有更新,仍然为4.1之前的16字节宽,当客户端使用PASSWORD()GRANTSET PASSWORD执行密码更改操作时,服务器注意到长哈希不适合,只生成短哈希。如果你已经升级到4.1但还没有运行 mysql_fix_privilege_tables脚本来扩宽Password列时会出现这种行为。

·         如果Password列足够宽,则可以保存短或长密码哈希值。在这种情况下,PASSWORD()GRANTSET PASSWORD生成长哈希,除非 用--old-passwords选项启动服务器。该选项强制服务器生成短密码哈希值。

--old-passwords选项的目的是当服务器生成长密码哈希值时,允许你维持同4.1之前的客户端的向后兼容性。该选项不影响鉴定(4.1和以后版本的客户端仍然可以使用有长密码哈希值的账户),但它防止在密码更改操作中在user表中创建长密码哈希值。在这种情况下,该账户不能再用于4.1之前的客户端。没有--old-passwords选项,可能会出现下面的不期望的情况:

·         旧客户端连接有短密码哈希值的账户。

·         客户更改自己的密码。没有--old-passwords,可以为该账户生成长密码哈希值。

·         下次旧客户试图连接账户时不能连接上,因为账户有长密码哈希值,需要新的哈希机制进行鉴定。(一旦账户user表中为长密码哈希值,只有4.1和以后版本的客户端可以鉴定它,因为4.1之前的客户端不理解长哈希)

该场景说明,如果你必须支持旧的4.1之前的客户端,不使用--old-passwords选项运行4.1或更新版本的服务器很危险。用--old-passwords运行服务器,密码更改操作不会生成长密码哈希值,这样旧客户端也可以访问账户。(这些客户端不能意外地因更改了密码将自己锁出去,并留下长密码哈希值)

--old-passwords选项的不利之处是你创建或更改的密码使用短哈希,甚至对于4.1客户端也如此。这样,你丢失了长密码哈希值提供的安全性。如果你想要创建有长哈希的账户(例如,为4.1客户端),你必须不使用--old-passwords来运行服务器。

下面的场景可用于运行4.1或以后的服务器,包括MySQL 5.1

场景1user表中的短Password列:

·         只有短哈希可以保存到Password列。

·         服务器只使用短哈希进行客户端鉴定。

·         对于连接的客户端,调用PASSWORD()GRANTSET PASSWORD的密码哈希生成操作专使用短哈希。对账户的任何更改均会生成短密码哈希值。

·          --old-passwords选项可以使用但是多余,因为Password列较短,服务器只生成短密码哈希值。

场景2Password列;没有用--old-passwords选项启动服务器:

·         短或长哈希可以保存到Password列。

·         4.1和以后版本的客户端(包括5.1客户端)可以鉴定有短或长哈希的账户。

·         4.1之前的客户端只能鉴定有短哈希的账户。

·         对于连接的客户端,调用PASSWORD()GRANTSET PASSWORD的密码哈希生成操作专使用短哈希。对账户的任何更改均会生成短密码哈希值。

如前面所示,该场景的危险性在于4.1之前的客户端可能不能访问有短密码哈希值的账户。通过PASSWORD()GRANTSET PASSWORDA对这些账户密码的更改会产生长的密码哈希值。从该点看,4.1之前的客户端升级到4.1之前不能鉴定该账户。

要处理该问题,可以用特殊方法更改密码。例如,一般情况你可以使用SET PASSWORD按照下面的方法更改账户密码:

mysql> SET PASSWORD FOR 'some_user'@'some_host' = PASSWORD('mypass');

要想更改密码但创建短哈希,使用OLD_PASSWORD()函数:

mysql> SET PASSWORD FOR 'some_user'@'some_host' = OLD_PASSWORD('mypass');

当你想明显生成短哈希时OLD_PASSWORD()很有用。

场景3Password列;用--old-passwords选项启动4.1或新版本的服务器:

·         短或长哈希可以保存到Password列。

·         4.1和以后版本的客户端可以鉴定有短或长哈希的账户(请注意只有不使用--old-passwords选项启动服务器,方可以创建长哈希)

·         4.1之前的客户端只可以鉴定短哈希账户。

·         对于连接的客户端,调用PASSWORD()GRANTSET PASSWORD的密码哈希生成操作专使用短哈希。对账户的任何更改均会生成短密码哈希值。

在该场景中,你不能创建长密码哈希值的账户,因为--old-passwords选项防止生成长哈希。并且,如果你在使用--old-passwords选项前创建长哈希账户,当--old-passwords有时更改账户密码,结果会使账户的密码为短密码,安全性较长哈希要降低。

这些场景的不利之处可以概括为:

在场景1,你不能利用长哈希提供更安全的鉴定。

在场景2, 如果你没有显式使用OLD_PASSWORD()来更改密码,则4.1之前的客户端不能再访问短哈希账户。

在场景3,--old-passwords防止短哈希账户不可访问,但密码更改操作使账户的长哈希转换为短哈希,当--old-passwords有效时不能将它改回长哈希。

5.7.9.1. 更改应用程序密码哈希值的含义

升级到MySQL4.1或更新版本后,使用PASSWORD()为自己的目的生成密码的应用程序会出现兼容性问题。应用程序实际不应这样做,因为PASSWORD()只应用来管理MySQL账户的密码。但一些应用程序使用PASSWORD()用于自己的目的。

如果你从MySQL 4.1之前的版本升级到4.1或以后版本,并在生成长密码哈希值的条件下运行服务器,应用程序使用PASSWORD()破解自己的密码。这种情况下推荐的方法是修改应用程序,使用其它函数,例如SHA1()MD5(),来产生哈希值。如果不行,你可以使用OLD_PASSWORD()函数,该函数用来提供旧格式的短哈希。但是,请注意OLD_PASSWORD()可能有一天不再被支持。

如果服务器运行在生成短哈希的条件下,可以使用 OLD_PASSWORD()但与PASSWORD()等同。

MySQL数据库从4.0或更低版本移植到4.1或更高版本的PHP编程人员应参阅旧客户端