PSI Labs RSS feed

PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT を設定して SSL証明書 の CN検証をパスする

こんにちは、tomitaです。

PDO_MySQL を使った SSL 接続で少しつまづいたのでメモがてら残しておきます。

環境は以下の通りです。

  • CentOS 7
  • MySQL 8.0.11
  • PHP 7.2.5

まず、MySQL のSSL接続設定を有効にします。

cd /var/lib/mysql
mysql_ssl_rsa_setup
chown mysql:mysql *.pem
systemctl restart mysqld

次にSSL接続用ユーザを作成します。

mysql -u root -p
> CREATE USER 'ssl'@'localhost' IDENTIFIED BY 'sslpassword' require SSL;
> CREATE USER 'ssl'@'127.0.0.1' IDENTIFIED BY 'sslpassword' require SSL;
> GRANT ALL PRIVILEGES ON *.* TO 'ssl'@'localhost';
> GRANT ALL PRIVILEGES ON *.* TO 'ssl'@"127.0.0.1";

以下で接続できれば準備完了です。

mysql -u ssl -p --ssl-mode=REQUIRED
(パスワード入力)

次に、以下のPHPスクリプト経由で接続してみます。

<?php
$dsn = "mysql:host=127.0.0.1;port=3306;dbname=testdb;charset=utf8mb4";
$user = "ssl";
$password = "sslpassword";
$options = [
  \PDO::MYSQL_ATTR_SSL_KEY  => "/var/lib/mysql/client-key.pem",
  \PDO::MYSQL_ATTR_SSL_CERT => "/var/lib/mysql/client-cert.pem",
  \PDO::MYSQL_ATTR_SSL_CA   => "/var/lib/mysql/ca.pem",
];

try{
  $P = new \PDO($dsn, $user, $password, $options);
} catch (\Exception $e) {
  do {
    printf("%s:%d %s\n", $e->getFile(), $e->getLine(), $e->getMessage());
  } while($e = $e->getPrevious());
  exit();
}

echo "success!!!\n";

実行すると以下エラーが発生しました。SSL証明書のCN検証でエラーが発生しています。

/home/tomita/work/ssl_con_test.php:12 SQLSTATE[HY000] [2002]
/home/tomita/work/ssl_con_test.php:12 PDO::__construct(): Peer certificate CN=`MySQL_Server_8.0.11_Auto_Generated_Server_Certificate' did not match expected CN=`127.0.0.1'

mysqli 関数経由の場合、SSL証明書の検証をパスできる MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT 定数が定義されているんですが、PDO_MYSQL には見つからない……。と思ったらドキュメントのコメント欄に以下の投稿がありました。なぜかドキュメント化されていないようです。

http://php.net/manual/ja/ref.pdo-mysql.php#122326

There is an important undocumented attribute which disables certificate CN verification available
5.6.22 (not sure), 7.0.18 (verified) and 7.1.15 (not sure)

PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT

possible values: true, false
default value: true

$options に \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false を追加、再度スクリプトを実行して「success!!!」表示を確認しました。

$options = [
  \PDO::MYSQL_ATTR_SSL_KEY  => "/var/lib/mysql/client-key.pem",
  \PDO::MYSQL_ATTR_SSL_CERT => "/var/lib/mysql/client-cert.pem",
  \PDO::MYSQL_ATTR_SSL_CA   => "/var/lib/mysql/ca.pem",
  \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, // ← これ追加
];

それでは~