PHP MySQL通过SSL连接。对等证书不匹配。

22

我正在尝试从GCE(Google Compute Engine)实例使用SSL连接到Google Cloud SQL。我的问题是无法通过SSL连接到Cloud SQL实例。

mysql命令正常工作。我可以使用认证文件连接到Cloud SQL实例。

mysql -uroot -p -h [IP Address] --ssl-ca=/home/user/.cert/server-ca.pem --ssl-cert=/home/user/.cert/client-cert.pem  --ssl-key=/home/user/.cert/client-key.pem

然而,当我从PHP程序访问时,我收到了以下警告和致命错误。

<?php
$pdo = new PDO('mysql:host=[IP Address];dbname=testdb', 'root', 'test', array(
    PDO::MYSQL_ATTR_SSL_KEY    =>'/home/user/.cert/client-key.pem',
    PDO::MYSQL_ATTR_SSL_CERT=>'/home/user/.cert/client-cert.pem',
    PDO::MYSQL_ATTR_SSL_CA    =>'/home/user/.cert/server-ca.pem'
    )
);
$stmt = $pdo->query("SHOW TABLES;");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($row);
?> 

 

PHP Warning:  PDO::__construct(): Peer certificate CN=`[GCP project name]:[Cloud SQL instance name]' did not match expected CN=`[IP Address]' in /tmp/mysql.php on line 7
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000] [2002] ' in /tmp/mysql.php on line 7

我在使用mysqli时遇到了相同的错误。

$mysqli = mysqli_init();
mysqli_options($mysqli, MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true);

$mysqli->ssl_set('/home/user/.cert/client-key.pem',
                 '/home/user/.cert/client-cert.pem',
                 '/home/user/.cert/server-ca.pem',
                  NULL,NULL);

$mysqli->real_connect('[IP Address]', 'root', 'test', 'testdb', 3306, NULL, MYSQLI_CLIENT_SSL);

 

PHP Warning:  mysqli::real_connect(): Peer certificate CN=`[GCP project name]:[Cloud SQL instance name]' did not match expected CN=`[IP Address]' in /tmp/mysql3.php on line 30
Warning: mysqli::real_connect(): (HY000/2002):  in /tmp/mysql3.php on line 30

这个问题看起来和我的案例相关,但是还没有答案。 SSL自签名证书用于与PHP连接Mysql

有人知道解决方案吗?


更新1

有人报告了这个错误。 https://bugs.php.net/bug.php?id=71003

在这里也有一个非常相似的问题。 Google Cloud SQL SSL验证对等证书失败

我的PHP版本是5.6.14。 我将升级到5.6.16,以使用MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT。


更新2

当我使用mysqli时已经解决了它。

我所做的如下:

1 我将我的PHP更新到5.6.20

sudo apt-get install php5

我将MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT选项设置为以下方式。

$mysqli->real_connect('[IP Address]', 'root', 'test', 'testdb', 3306, NULL, MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT);

我的应用程序由于某些原因同时使用mysqli和PDO。现在我正在寻找PDO的解决方案。


更新3:

这个错误报告涉及到PDO的情况,似乎还没有被修复。

https://bugs.php.net/bug.php?id=71845

这也与之相关:https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/google-cloud-sql-discuss/4HNvmq7MpU4/kuSjhkS2AwAJ

据我所知,目前没有解决PDO的方法。


更新4:

一些人指责谷歌的CN名称设计(我实际上同意他们的观点...)

最糟糕的是你们使用不可能的CN名称(:)。如果CN没有冒号,也许我可以在我的主机文件中映射ip和CN,这样当进行对等验证时就可以通过了。有了冒号,php认为是主机,是端口。

而且谷歌员工也理解了这个问题。

我理解通过IP连接的当前情况并不理想。

但看起来他们提供了一个叫做“代理”的解决方案。

https://groups.google.com/forum/#!topic/google-cloud-sql-discuss/gAzsuCzPlaU

我正在使用第二代Cloud SQL,并且我的应用程序托管在GCE上。因此,我认为我可以使用代理方式。我现在要尝试一下。


更新5:

设置代理访问。已解决PDO和mysqli访问。

在Ubuntu上安装代理。

$ wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64
$ mv cloud_sql_proxy.linux.amd64 cloud_sql_proxy
$ chmod +x cloud_sql_proxy
$ sudo mkdir /cloudsql; sudo chmod 777 /cloudsql
$ ./cloud_sql_proxy -dir=/cloudsql -instances=<project name>:us-central1:mydb 

PDO

<?php
$pdo = new pdo('mysql:unix_socket=/cloudsql/<project name>:us-central1:mydb;dbname=testdb','root','test');
$stmt = $pdo->query("SHOW TABLES;");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($row);
?>

mysqli

$mysqli = mysqli_connect('localhost', 'root', 'test', 'testdb', 3306, '/cloudsql/<project name>:us-central1');
$sql = "SELECT id FROM users";
if ($result = $mysqli->query($sql)) {
    while ($row = $result->fetch_assoc()) {
        echo $row["id"] . "\n";
    }
    $result->close();
}
$mysqli->close();

参考 (日语)

连接Google Cloud SQL使用MySQL客户端的方法 在Google App Engine上使用Cloud SQL的PHP方法


你很棒。更新#5对我来说完全有效。谢谢!!! - ingernet
我曾经苦思冥想了好几个小时。也许这会帮助其他人。第5步骤中的第1部分是我的问题 - 我是以root身份登录的。以普通用户重新运行后,一切按预期工作! - webaholik
5个回答

15

1
这是否意味着连接已经安全? - Yetispapa
是的,这是一个加密连接,但需要注意的是您没有验证证书本身。 - Javi
我知道,但是我找不到任何不使用上面那行代码的解决方案。当在终端或类似Sequel的应用程序中使用证书时,它们可以正常工作。但在PHP方面,我找不到任何解决方案。 - Yetispapa
这仅适用于使用PHP 7.1.x的情况。如果使用Azure,在Ubuntu上部署的好方法是确保使用Ubuntu 18LTS。这提供了正确版本的PHP的默认值,该版本具有PDO :: MYSQL_ATTR_SSL_VERIFY_SERVER_CERT。 - CarComp
我正在使用php 5.6.40,但不支持PDO :: MYSQL_ATTR_SSL_VERIFY_SERVER_CERT。是否有其他解决方案可用于PDO? 谢谢 - ytdm
显示剩余2条评论

4

对于mysqli,添加:

MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT

real_connect()函数解决了我的这个问题。

示例(根据需要替换主机、用户、密码和数据库):

$db_connection->real_connect('ip address or host', 'user', 'password', 'database', 3306, NULL, MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT);

完整的php mysqli SSL连接脚本:

// check if openssl is enabled on server
if(!extension_loaded('openssl')) {
    throw new Exception('This app needs the Open SSL PHP extension and it is missing.');
}

// start connection
$db_connection = mysqli_init(); 

// set ssl config (update with your certs location)
$db_connection->ssl_set('/etc/my.cnf.d/certs/client-key.pem','/etc/my.cnf.d/certs/client-cert.pem', '/etc/my.cnf.d/certs/ca-cert.pem', NULL, NULL); 

// connect (update with your host, db and credentials)
$db_connection->real_connect('ip address or host', 'user', 'password', 'database', 3306, NULL, MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT);

4

3
GCP员工请注意:公关不是编辑答案的合理理由。 - Sammitch

1
CakePHP 3.5.x + PHP 7.1.x + SQL Google Cloud + SSL的示例。
在config/app.php文件中进行更改。
    ...
    ...

    /**
     * The test connection is used during the test suite.
     */
    'default' => [
        'className' => 'Cake\Database\Connection',
        'driver' => 'Cake\Database\Driver\Mysql',
        'persistent' => false,
        'host' => 'xx.xxx.xxx.xxx',
        //'port' => 'non_standard_port_number',
        'username' => 'user_name_x',
        'password' => 'pass_x',
        'database' => 'bd_name_x',
        'encoding' => 'utf8',
        'timezone' => 'UTC',
        'cacheMetadata' => true,
        'quoteIdentifiers' => false,
        'log' => false,

        'flags' => [
           PDO::MYSQL_ATTR_SSL_KEY  => CONFIG.'client-key.pem',
           PDO::MYSQL_ATTR_SSL_CERT => CONFIG.'client-cert.pem',
           PDO::MYSQL_ATTR_SSL_CA   => CONFIG.'server-ca.pem',
           PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false
        ],

        //'ssl_key' => CONFIG.'client-key.pem',
        //'ssl_cert' => CONFIG.'client-cert.pem',
        //'ssl_ca' => CONFIG.'server-ca.pem',

        //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
    ],
 ],
 ...
 ...

例子 PHP Pure + PDO + SSL:

$ssl_key = CONFIG.'client-key.pem';
$ssl_cert = CONFIG.'client-cert.pem';
$ssl_ca = CONFIG.'server-ca.pem';

$pdo = new PDO('mysql:host=xxx.xxx.xxx.xxx;dbname=db_name_x', 'user_name_x', 'pass_x', array(
    PDO::MYSQL_ATTR_SSL_KEY   => '/path/full/client-key.pem',
    PDO::MYSQL_ATTR_SSL_CERT  => '/path/full/client-cert.pem',
    PDO::MYSQL_ATTR_SSL_CA    => '/path/full/server-ca.pem',
    PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false
    )
);
$statement = $pdo->query("SHOW TABLES;");
var_dump($statement);
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo json_encode($row);
exit();

0

我在Compose集群上托管的MySQL数据库遇到了同样的问题。

经过数小时的搜索和测试,我发现最好的解决方案是使用MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT标志禁用测试服务器提供的证书。

我没有用PDO进行测试,但是对于mysqli接口,它对我有效。

通常,这个问题会出现在托管在集群服务器上的数据库中。


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