phpMyAdmin 启用 mcrypt #1045 无法登录 MySQL 服务器 Cannot log in to the MySQL server
2017-10-31 14:29:08 旧日重来
安装好了 phpMyAdmin,运行正常,但登录之后提示建议启用 mcrypt。按照提示一路 yum install php-mcrypt
,启用后却发现无法登录了,提示 #1045 Cannot log in to the MySQL server (#1045 无法登录 MySQL 服务器)。简单排查未发现明显问题,只好通过 debug 源码解决,记录在这里
出现这个错误一般有以下几类原因:
- MySQL 服务未启动或异常
- php-mysql/php-mysqli/php-mysqlnd 库和配置文件不匹配
- 连接配置有误,如 ip 地址/Linux socket地址不匹配。特别的,如果是支持 IPv6的机器(新版系统一般都支持),则监听地址配了 127.0.0.1 而 phpMyAdmin 配的是 localhost 则可能无法连接
由于安装 php-mcrypt 之前是可以登录的,所以肯定不是上面几个常见原因。而 phpMyAdmin 又没有打印有用的出错日志,一时没有头绪。只好从源代码入手开始 debug 了。
phpMyAdmin 的代码很复杂,为快速入手,首先在所有源代码中搜索出错信息的字符串,很快就在 ./libraries/plugins/auth/AuthenticationCookie.class.php
里发现了:
public function authFails(): // Other stuff $conn_error = '#' . $GLOBALS['errno'] . ' ' . __('Cannot log in to the MySQL server');
authFails()
函数本身是一个出错时打印信息的回调,而非真正出错的地方,所以并没有提供什么有用的信息。不过从这里入手可以进一步定位,php 内部提供了获取函数 callstack (调用栈) 的功能,所有只要在 authFails()
内部查看 callstack,就可以进一步找到出错的地点了。简单起见,这里直接调用 debug_print_backtrace()
来实现:
public function authFails(): // Other stuff debug_print_backtrace(); // 打印 callstack,added by Aulddays $conn_error = '#' . $GLOBALS['errno'] . ' ' . __('Cannot log in to the MySQL server');
注意,不要在生产环境中直接调用debug_print_backtrace()
,它把信息直接打印到网页上,可能会暴露内部信息。生产环境的话可以考虑用debug_backtrace()
获取再通过error_log()
之类打印到仅内部可见的地点
callstack 如下:
#0 AuthenticationCookie->authFails() called at [/live.aulddays.com/pma/libraries/dbi/mysql.dbi.lib.php:179] #1 PMA_DBI_connect(, testp, ) called at [/live.aulddays.com/pma/libraries/common.inc.php:978] #2 require_once(/live.aulddays.com/pma/libraries/common.inc.php) called at [/live.aulddays.com/pma/index.php:11]
可以看见,实际的出错地点是 PMA_DBI_connect()
函数。再仔细观察,此函数的原型是:
function PMA_DBI_connect($user, $password, ...)
但 callstack 中实际参数只有 $password
,而 $user
是空串,说明用户名在某个地方丢失了。接着查找,发现用户名是在下面这个函数中从浏览器 cookie 解析的:
public function cookieDecrypt($encdata, $secret)
通过 firefox 的 developer tool 查看,站点 cookie 中 pmaUser-1
确实为空,而 pmaAuth-1
是有内容的:
说明在创建 cookie 时用户名就已经丢失。跟着上面的 Decrypt()
函数顺藤摸瓜,不难找到对应的 Encrypt()
函数:
public function cookieEncrypt($data, $secret)
该函数功能很简单,代码就不贴了,具体就是看一下是否安装了 mcrypt,如果是就用 mcrypt_encrypt()
来加密,否则调用 phpMyAdmin 自带的加密函数来实现。查看文档,mcrypt_encrypt()
函数成功返回加密结果,失败返回 false
。而 phpMyAdmin 在这里并未检查返回值,而 php 内部把 false
转为了空串造成问题。下面就继续看一下为什么出错。这里把 cookieEncrypt()
的相关内容单独提出来测试一下,创建 test.php
,内容如下:
srand((double) microtime() * 1000000); $td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, ''); if ($td === false) { PMA_fatalError(__('Failed to use Blowfish from mcrypt!')); } $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); $result = mcrypt_encrypt(MCRYPT_BLOWFISH, "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdsadfasdf", "test", MCRYPT_MODE_CBC, $iv);
运行发现报错如下:
PHP Warning: mcrypt_encrypt(): Size of key is too large for this algorithm in /live.aulddays.com/test.php on line 8 PHP Catchable fatal error: mcrypt_encrypt(): Mcrypt initialisation failed in /live.aulddays.com/test.php on line 8
加密的 key 太长导致加密失败。而这里的 key 就是 config.ini.php
中配置的 $cfg['blowfish_secret']
。原来 Aulddays 把这个 key 写的太长,造成 mcrypt 运行出错,改短之后就正常了。经过进一步测试,mcrypt 可接受的最大长度是 56 字节。
有趣的是,phpMyAdmin 对这个字符串有最短长度的检查,而未作最大长度检查。另一方面,phpMyAdmin 内部实现的加密代码在长度超长时仍能正常运行。
查看:原文地址;来源:live.aulddays.com。
注意:本站所有文章除特别说明外均为原创,版权所有,转载请务必以超链接方式注明作者出处,并禁止用作商业用途