PHP连接MySQL时忽略SSL证书

14

使用PHP手册中这里这里找到的代码。

<?php

$mysqli = mysqli_init();
if (!$mysqli) {
    die('mysqli_init failed');
}

$mysqli->ssl_set('/path/to/client-key.pem',   
                 '/path/to/client-cert.pem',
                 '/path/to/ca-cert.pem',
                  NULL,NULL); //<-- Doesn't matter if the paths are right or wrong

if (!$mysqli->options(MYSQLI_INIT_COMMAND, 'SET AUTOCOMMIT = 0')) {
    die('Setting MYSQLI_INIT_COMMAND failed');
}

if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5)) {
    die('Setting MYSQLI_OPT_CONNECT_TIMEOUT failed');
}

if (!$mysqli->real_connect('xx.xx.xx.xx', 'my_user', 'my_password', 'my_db')) {
    die('Connect Error (' . mysqli_connect_errno() . ') '
            . mysqli_connect_error());
}

echo 'Success... ' . $mysqli->host_info . "\n";

$mysqli->close();
?>

我无法连接到MySql数据库,出现以下错误信息:
Message: SQLSTATE[28000] [1045] Access denied for user 'my_user'@'xx.xx.xx.xx' (using password: YES) <-- the client IP

我已将问题缩小到$mysqli->ssl_set(...)这一行。
根据这篇帖子,如果我传递错误的证书路径,它应该抛出"SSL证书错误",而不是之前的"拒绝访问"错误。但实际上,它仍然抛出之前的错误。
通过以下配置,我与数据库管理员的帮助成功使用Navicat连接到服务器: enter image description here 注:
1. 我的PHP版本为5.3.15。 2. 远程数据库服务器使用的MySql版本为5.5.13-55。 3. Navicat和PHP代码都在同一台PC上运行。 4. 数据库管理员专门为测试创建了一个ssl_user用户。该用户没有正常访问权限,必须使用ssl连接。 5. 没有使用代理。 6. 我尝试了PDO,但遇到了相同的问题。 7. 我尝试了mysql->connect(),但它会抛出一个握手错误,根据手册的一些评论,它不起作用。 8. 从phpinfo()中启用了Open ssl。 enter image description here 因此,问题是为什么PHP没有抱怨证书路径错误?看起来PHP正在忽略我设置证书路径的那一行!
$mysqli->real_connect('xx.xx.xx.xx', 'my_user', 'my_password', 'my_db','3306',NULL,MYSQLI_CLIENT_SSL)

然而,我遇到了这个错误:
Warning: mysqli::real_connect(): (08S01/1043): Bad handshake in ...

更新2:

这是从数据库服务器中tcpdump的结果:

0x0040:  3142 6164 2068 616e 6473 6861 6b65       1Bad.handshake
13:16:18.133282 IP (tos 0x8, ttl  64, id 40823, offset 0, flags [DF], proto: TCP (6), length: 52) xx.xx.xx.202.mysql > xx.xx.xx.130.42766: F, cksum 0xf15f (correct), 112:112(0) ack 79 win 46 <nop,nop,timestamp 19516376 3465907>
        0x0000:  4508 0034 9f77 4000 4006 c8a7 c0a8 28ca  E..4.w@.@.....(.
        0x0010:  c0a8 2882 0cea a70e b36c 7e8b 7b4d a169  ..(......l~.{M.i
        0x0020:  8011 002e f15f 0000 0101 080a 0129 cbd8  ....._.......)..
        0x0030:  0034 e2b3                                .4..
13:16:18.133433 IP (tos 0x8, ttl  64, id 17337, offset 0, flags [DF], proto: TCP (6), length: 52) xx.xx.xx.130.42766 > xx.xx.xx.202.mysql: F, cksum 0xb695 (correct), 79:79(0) ack 113 win 92 <nop,nop,timestamp 3480910 19516376>
        0x0000:  4508 0034 43b9 4000 4006 2466 c0a8 2882  E..4C.@.@.$f..(.
        0x0010:  c0a8 28ca a70e 0cea 7b4d a169 b36c 7e8c  ..(.....{M.i.l~.
        0x0020:  8011 005c b695 0000 0101 080a 0035 1d4e  ...\.........5.N
        0x0030:  0129 cbd8                                .)..
13:16:18.133452 IP (tos 0x8, ttl  64, id 40824, offset 0, flags [DF], proto: TCP (6), length: 52) xx.xx.xx.202.mysql > xx.xx.xx.130.42766: ., cksum 0xb6c3 (correct), 113:113(0) ack 80 win 46 <nop,nop,timestamp 19516376 3480910>
        0x0000:  4508 0034 9f78 4000 4006 c8a6 c0a8 28ca  E..4.x@.@.....(.
        0x0010:  c0a8 2882 0cea a70e b36c 7e8c 7b4d a16a  ..(......l~.{M.j
        0x0020:  8010 002e b6c3 0000 0101 080a 0129 cbd8  .............)..
        0x0030:  0035 1d4e                                .5.N

xx.xx.xx.202是数据库服务器的IP地址。
xx.xx.xx.130是我的PC的IP地址。

更新3:

我使用以下代码检查密码长度:

mysql> select length(Password) from mysql.user where User='youruser'

密码长度为41

更新4:

当我通过远程服务器上的命令行使用ssh连接时(我将整个项目上传到了远程服务器),返回的错误消息中可能会有提示:

mysql -u test_ssl -password --ssl-key=/usr/local/apache2/htdocs/certs/client-key.pem --ssl-cert=/usr/local/apache2/htdocs/certs/WRONG-CERTIFICATE.pem

我遇到了以下错误:

SSL error: Unable to get certificate from '/usr/local/apache2/htdocs/certs/WRONG-CERTIFICATE.pem'
ERROR 2026 (HY000): SSL connection error ---> notice the error here

如果我没有提供任何证书,我会收到以下信息:
-bash-3.2$ mysql -u test_ssl -password
ERROR 1045 (28000): Access denied for user 'test_ssl'@'localhost' (using password: YES)

当我使用正确或无效的证书路径连接时,使用PHP连接时会出现相同的错误!

也许这意味着什么?

2个回答

3

乍一看,您没有设置连接的MYSQLI_CLIENT_SSL选项。

if (!$mysqli->real_connect('localhost', 'my_user', 'my_password', 'my_db', null, MYSQLI_CLIENT_SSL)) { ... }

更新:
您可以尝试在$mysqli->ssl_set()之前添加$mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true);

此外,这对我有用:

<?php
$pdo = new PDO('mysql:host=ip;dbname=dbname', 'username', 'password', array(
    PDO::MYSQL_ATTR_SSL_KEY    =>'/etc/mysql/ssl/client-key.pem',
    PDO::MYSQL_ATTR_SSL_CERT=>'/etc/mysql/ssl/client-cert.pem',
    PDO::MYSQL_ATTR_SSL_CA    =>'/etc/mysql/ssl/ca-cert.pem'
    )
);
$stmt = $pdo->query("SHOW TABLES;");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($row);
?>

更新2:如果您使用旧的 mysql 密码(长度为16字节),您可能会收到一个“bad handshake”的错误提示。要检查,请运行以下查询:

mysql> select length(Password) from mysql.user where User='youruser'

如果您收到 16
// disable old_passwords:
mysql> SET @@session.old_passwords = 0;

// update password for user used in PHP script:
mysql> SET PASSWORD FOR 'existinguser'@'localhost' = PASSWORD('existingpass');

// check if we have a 41 bytes long password:
mysql> select length(Password) from mysql.user where User='youruser'

再次检查来自PHP脚本的连接。


我添加了MYSQLI_OPT_SSL_VERIFY_SERVER_CERT这一行,但是仍然抛出相同的(08S01/1043): Bad handshake in ...错误。当我从real_connect()中移除MYSQLI_CLIENT_SSL时,它会给出旧的Access denied错误。最后,我尝试了几天的PDO版本,但是它仍然给出相同的Access denied错误。 - Songo
@Songo:你能否请再次检查服务器的SSL配置。你可以使用这篇文章作为参考。 - Alexandru Guzinschi
好的,我会仔细检查一下。虽然我能够使用Navicat连接,这真的很奇怪... - Songo
@Songo 你还在和错误斗争吗?当我无法使用ssh连接服务器时,我解决了这个问题,通过转储ssh会话并快速查看出错原因。尝试找到一种方法来转储会话并找出它断开的位置。目前,我们是盲目的。 - user4035
@user4035 嗯,这真是让我抓狂 :D 我正在尝试使用你提到的tcpdump,但首先我需要系统管理员的权限,而他目前很忙 :S - Songo
@vimishor 我尝试了你的新代码来检查密码长度,结果是 41。好吧,我不认为问题在 MySql 中,因为我能够使用 Navicat 连接到数据库,所以我要解决的是如何修复 PHP 中的连接请求。 - Songo

1
我们成功解决了问题,但我不确定这是否是一个实际可行的解决方案。
首先,我们能够找到问题的源头,即MySql客户端库,PHP使用这些库来使用SSL进行连接。必须使用这些库来编译PHP才能使用SSL进行连接。
此外,MySql客户端库与OpenSSL库之间存在冲突。
我们的唯一解决方案是从头重新安装所有内容。我们使用了一个新的服务器,最新版本的Mysql安装包及其所有库,最新版本的PHP构建和最新版本的CentOS发行版。

你能详细说明一下使用了哪些库以及如何编译PHP吗?我也遇到了同样的问题和情况。我可以通过终端和MySQL Workbench连接,但是无法使用PHP连接,而且我需要不断地告诉数据库管理员发送日志和转储文件,但这并没有帮助我解决问题。 - Silencer310

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接