一般的にはデータベースでは機密性の高いデータを管理することが多く、セキュリティについても十分に注意を払った設計を行う必要があります。
今回は MySQLサーバーとクライアントとの接続に SSL/TLS を使った暗号化した接続を行う方法をご紹介したいと思います。
参考:MySQL :: MySQL 5.7 Reference Manual :: 6.4 Using Encrypted Connections
MySQL の SSL/TLS について
MySQLではSSL/TLSのライブラリとして OpenSSL と yaSSL がサポートされていますが、インストールの方法やバージョンによって組み込まれるライブラリが異なります。
バイナリ | ライブラリ |
---|---|
MySQL 8.0 | OpenSSL |
MySQL 5.7以前の商用版 | OpenSSL |
MySQL 5.7以前のCommunity | yaSSL |
MySQL5.7以前の Community Edition でも ソースからコンパイルすると OpenSSL が使えます。
OpenSSL と yaSSL では以下のような違いがあります。(MySQL 5.7.21の場合)
OpenSSL | yaSSL | |
---|---|---|
暗号化方式 | 45種類 | 16種類 |
バージョン | TLS1.2 TLS1.1 TLS1.0 | TLS1.1 TLS1.0 |
大きな違いとしてはOpenSSLのみ最新のTLS1.2を使うことができることと
yaSSLの場合、暗号利用モードに脆弱性を含むCBC (Cipher Block Chaining)が暗号化方式として有効になっている点です。
TLS1.1、1.0ではPOODLEと呼ばれる脆弱性を含んでいるため、セキュリティを重要視するならTLS1.2を利用することが望ましいです。
参考
MySQL :: MySQL 5.7 Reference Manual :: 6.4.4 OpenSSL Versus yaSSL
ImperialViolet – The POODLE bites again
サーバー設定
ここからのセクションは MySQL 5.7 の商用版を使っています。基本的な動作はコミュニティ版と同じです。
暗号鍵の作成
暗号鍵の作成は OpenSSL コマンドを使って生成する方法もありますが、MySQL 5.7.6 からは mysql_ssl_rsa_setup コマンドが同梱されており、こちらを使うと簡単に必要な鍵を生成することができます。
また、初回起動時に systemd の設定を見ると、自動的に鍵生成が行われます。
1 2 3 4 5 6 7 |
$ cat /usr/bin/mysqld_pre_systemd ...省略... # Generate certs if needed if [ -x /usr/bin/mysql_ssl_rsa_setup -a ! -e "${datadir}/server-key.pem" ] ; then /usr/bin/mysql_ssl_rsa_setup --datadir="$datadir" --uid=mysql >/dev/null 2>&1 fi ...省略... |
MySQL のデータディレクトリ以下に適切なファイル名で生成されるので、my.cnf に設定を追加しなくても、SSL/TLS 設定が有効になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> show global variables like '%ssl%'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | have_openssl | YES | | have_ssl | YES | | ssl_ca | ca.pem | | ssl_capath | | | ssl_cert | server-cert.pem | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | server-key.pem | +---------------+-----------------+ 9 rows in set (0.00 sec) |
ここでは、改めて暗号鍵を再作成するところから始めます。
まずは、データディレクトリの鍵ファイルを削除します
1 |
$ sudo rm -rf /var/lib/mysql/*.pem |
mysql_ssl_rsa_setup を実行して再度鍵ファイルを生成します
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ sudo mysql_ssl_rsa_setup --uid=mysql Generating a 2048 bit RSA private key ......+++ ...........+++ writing new private key to 'ca-key.pem' ----- Generating a 2048 bit RSA private key .............+++ ....+++ writing new private key to 'server-key.pem' ----- Generating a 2048 bit RSA private key ..........+++ ...........+++ writing new private key to 'client-key.pem' ----- |
MySQLのデータディレクトリに以下のような鍵ファイルが作成されます。
1 2 3 4 5 6 7 8 9 |
$ sudo ls -l /var/lib/mysql/*.pem -rw-------. 1 mysql mysql 1679 Mar 28 05:51 /var/lib/mysql/ca-key.pem -rw-r--r--. 1 mysql mysql 1107 Mar 28 05:51 /var/lib/mysql/ca.pem -rw-r--r--. 1 mysql mysql 1107 Mar 28 05:51 /var/lib/mysql/client-cert.pem -rw-------. 1 mysql mysql 1675 Mar 28 05:51 /var/lib/mysql/client-key.pem -rw-------. 1 mysql mysql 1679 Mar 28 05:51 /var/lib/mysql/private_key.pem -rw-r--r--. 1 mysql mysql 451 Mar 28 05:51 /var/lib/mysql/public_key.pem -rw-r--r--. 1 mysql mysql 1107 Mar 28 05:51 /var/lib/mysql/server-cert.pem -rw-------. 1 mysql mysql 1675 Mar 28 05:51 /var/lib/mysql/server-key.pem |
ここで生成されるCA(Certification Authority、認証局)は自己認証局です。
鍵の有効期間は10年で作成されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: CN=MySQL_Server_5.7.21_Auto_Generated_CA_Certificate Validity Not Before: Apr 5 06:06:44 2018 GMT Not After : Apr 2 06:06:44 2028 GMT Subject: CN=MySQL_Server_5.7.21_Auto_Generated_CA_Certificate Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c4:08:fd:5b:2d:71:b8:21:85:c4:15:d4:07:f6: ...省略... 44:6f Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha256WithRSAEncryption 0b:55:be:03:4a:9b:e7:f9:17:7b:c1:e3:8f:3b:ae:a7:06:27: ...省略... d5:c7:52:76 |
my.cnf に暗号鍵のパスを指定します。
1 2 3 4 |
[mysqld] ssl-ca=/var/lib/mysql/ca.pem ssl-cert=/var/lib/mysql/server-cert.pem ssl-key=/var/lib/mysql/server-key.pem |
MySQLを再起動します。
1 |
$ sudo systemctl restart mysqld |
SSL/TLS が有効になっているかを確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> show global variables like '%ssl%'; +---------------+--------------------------------+ | Variable_name | Value | +---------------+--------------------------------+ | have_openssl | YES | | have_ssl | YES | | ssl_ca | /var/lib/mysql/ca.pem | | ssl_capath | | | ssl_cert | /var/lib/mysql/server-cert.pem | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | /var/lib/mysql/server-key.pem | +---------------+--------------------------------+ 9 rows in set (0.01 sec) |
セキュリティの強化
現状の設定ではTLSのバージョンは1.0から1.2まで使用することができます。
1 2 3 4 5 6 7 |
mysql> show global variables like 'tls_version'; +---------------+-----------------------+ | Variable_name | Value | +---------------+-----------------------+ | tls_version | TLSv1,TLSv1.1,TLSv1.2 | +---------------+-----------------------+ 1 row in set (0.01 sec) |
ここではmy.cnf を設定変更してTLSのバージョンを1.2のみに設定変更します。
1 2 |
[mysqld] tls_version=TLSv1.2 |
MySQLを再起動します。
1 |
$ sudo systemctl restart mysqld |
設定変更を確認
1 2 3 4 5 6 7 |
mysql> show global variables like 'tls_version'; +---------------+---------+ | Variable_name | Value | +---------------+---------+ | tls_version | TLSv1.2 | +---------------+---------+ 1 row in set (0.00 sec) |
クライアント設定
MySQLではユーザー設定でSSL/TLSによる接続方法を指定しない限り、クライアント側はどのように接続するかを決めることができます。
MySQLコマンドでは--ssl-mode
で指定することができます。
オプション | SSL/TLS接続 | |
---|---|---|
PREFFERED | 優先(デフォルト) | |
REQUIRED | 必須 | |
DISABLED | 使用しない | |
VERIFY_CA | 必須 | CA鍵による検証も行う |
VERIFY_IDENTITY | 必須 | クライアント認証も行う |
REQUIRED による接続
サーバー認証を行わず単純に通信を暗号化したい場合です。
この場合、中間者攻撃(man-in-the-middle attack)に対しては有効ではありません。
1 2 3 4 5 |
$ mysql -u root -p -h 127.0.0.1 --ssl-mode=REQUIRED mysql> \s ...省略... SSL: Cipher in use is DHE-RSA-AES128-GCM-SHA256 ...省略... |
DISABLEDによる接続
SSL/TLSを使わずに接続する場合です。
1 2 3 4 5 |
$ mysql -u root -p -h 127.0.0.1 --ssl-mode=DISABLED mysql> \s ...省略... SSL: Not in use ...省略... |
VERIFY_CAによる接続
サーバー認証を行なって接続する場合です。
1 |
$ mysql -u root -p -h 127.0.0.1 --ssl-mode=VERIFY_CA --ssl-ca=/var/lib/mysql/ca.pem |
VERIFY_IDENTITY による接続
サーバー認証とクライアント認証(相互認証)を行って接続する場合です。
MySQLサーバーへ接続するクライアントも鍵による認証を行います。
1 |
$ mysql -u root -p -h 127.0.0.1 --ssl-mode=VERIFY_IDENTITY --ssl-ca=/var/lib/mysql/ca.pem --ssl-cert=/var/lib/mysql/client-cert.pem --ssl-key=/var/lib/mysql/client-key.pem |
参考 6.4.1 Configuring MySQL to Use Encrypted Connections
TLSバージョンと暗号化アルゴリズム
暗号化アルゴリズムはこのリストの先頭からクライアントとサーバーの両方で対応しているものが使われます。
1 2 3 4 5 |
mysql> show session status like 'ssl_cipher_list%'\G *************************** 1. row *************************** Variable_name: Ssl_cipher_list Value: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-DSS-AES128-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:DH-DSS-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:AES256-GCM-SHA384:DH-DSS-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:AES128-SHA256:DH-DSS-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:AES256-SHA256:DH-DSS-AES256-SHA256:ECDH-ECDSA-AES256-SHA384:AES128-SHA:DH-DSS-AES128-SHA:ECDH-ECDSA-AES128-SHA:AES256-SHA:DH-DSS-AES256-SHA:ECDH-ECDSA-AES256-SHA:DHE-RSA-AES256-GCM-SHA384:DH-RSA-AES128-GCM-SHA256:ECDH-RSA-AES128-GCM-SHA256:DH-RSA-AES256-GCM-SHA384:ECDH-RSA-AES256-GCM-SHA384: 1 row in set (0.00 sec) |
現在の接続で使われている暗号とTLSのバージョンは以下のコマンドでわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> show session status like 'Ssl_cipher'; +---------------+---------------------------+ | Variable_name | Value | +---------------+---------------------------+ | Ssl_cipher | DHE-RSA-AES128-GCM-SHA256 | +---------------+---------------------------+ 1 row in set (0.00 sec) mysql> show session status like 'Ssl_version'; +---------------+---------+ | Variable_name | Value | +---------------+---------+ | Ssl_version | TLSv1.2 | +---------------+---------+ 1 row in set (0.00 sec) |
サーバーが対応している暗号化方式やプロトコルのバージョンから、クライアントは選択することになるため、悪意あるクライアントが最も弱い形式の暗号化方式を使うことでセキュリティ上の問題が発生する可能性があります。
my.cnf の設定でTLSのバージョンや使用できる暗号化方法を指定することができます。
1 2 3 |
[mysqld] tls_version=TLSv1.2 ssl_cipher=DHE-RSA-AES128-GCM-SHA256:AES128-SHA |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> SHOW GLOBAL VARIABLES LIKE 'tls_version'; +---------------+---------+ | Variable_name | Value | +---------------+---------+ | tls_version | TLSv1.2 | +---------------+---------+ 1 row in set (0.01 sec) mysql> show session status like 'ssl_cipher_list'; +-----------------+---------------------------------+ | Variable_name | Value | +-----------------+---------------------------------+ | Ssl_cipher_list | AES128-SHA256:AES128-GCM-SHA256 | +-----------------+---------------------------------+ 1 row in set (0.00 sec) |
MySQLのユーザー設定
MySQLのユーザーについても、SSL/TLS認証を必須にしない限り、クライアントがSSL/TLSによる接続を使わずに接続することが可能です(–ssl-mode=DISABLED)
そのため、MySQLのユーザーについても必要に応じて設定が必要です。
SSL/TLS認証を必須とするユーザーを作成する
1 2 |
mysql> create user user1 identified by 'Password1!' require ssl; Query OK, 0 rows affected (0.00 sec) |
SSL/TLS認証を使うとログインすることができます
1 2 |
$ mysql -u user1 -p -h 127.0.0.1 --ssl-mode=REQUIRED mysql> |
SSL/TLS認証を使わずにログインしようとすると認証エラーとなります
1 2 3 |
$ mysql -u user1 -p -h 127.0.0.1 --ssl-mode=DISABLED mysql: [Warning] Using a password on the command line interface can be insecure. ERROR 1045 (28000): Access denied for user 'user1'@'localhost' (using password: YES) |
クライアント認証(相互認証)を必須とする場合はX509
を指定します。
1 2 |
mysql> create user user2 identified by 'Password1!' require X509; Query OK, 0 rows affected (0.00 sec) |
クライアント認証を行わないとエラーになります。
1 2 3 |
$ mysql -u user2 -p -h 127.0.0.1 --ssl-mode=REQUIRED mysql: [Warning] Using a password on the command line interface can be insecure. ERROR 1045 (28000): Access denied for user 'user2'@'localhost' (using password: YES) |
クライアント鍵で認証を行うとログインすることができます。
1 2 |
$ mysql -u user2 -p -h 127.0.0.1 --ssl-ca=/var/lib/mysql/ca.pem --ssl-cert=/var/lib/mysql/client-cert.pem --ssl-key=/var/lib/mysql/client-key.pem mysql> |
PHPでSSL/TLS接続を使ってMySQLに接続する
今回は、 mysql_ssl_rsa_setup で生成された鍵を使ってPHPとMySQLをSSL/TLS接続する方法と、ベンチマークを行ってみたいと思います。
クライアント認証に必要な鍵ファイルはMySQLのデータディレクトリに以下のファイル名で生成されています。
1 2 3 |
client-key.pem client-cert.pem ca.pem |
PDO::MySQLを使った接続の場合、PHPのコードは以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 |
$dbh = new PDO( "mysql:host=localhost;dbname=test", root, Password, [ PDO::MYSQL_ATTR_SSL_KEY => '/path/to/client-key.pem', PDO::MYSQL_ATTR_SSL_CERT => '/path/to/client-cert.pem', PDO::MYSQL_ATTR_SSL_CA => '/path/to/ca.pem', PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, ] ); |
mysql_ssl_rsa_setup で生成されたCAは自己認証局なので、 PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT を無効にします。
SSL/TLS接続のベンチマーク
MySQLの接続処理は比較的軽量ですが、SSL/TLS接続は接続時のハンドシェイク処理にどうしても時間がかかってしまいます。今回はSSL/TLS接続を有効にした場合、どれくらい時間がかかるか計測してみました。
検証環境
検証で使用したマシンは、クライアント側(PHP)とサーバー側(MySQL)の2台の別筐体でローカルネットワークで繋がっています。
PHPのバージョン: 7.2.7
MySQLのバージョン: 5.7.22
ベンチマークのPHPコードでは、PDOを使って単純に接続と切断を1万回繰り返します。
また、接続方法については下記の3つの方法を試しました。
- 暗号化無し
- クライアント認証を含むSSL/TLS接続
- クライアント認証を含むSSL/TLS接続 + 持続的接続
持続的接続はMySQLとの接続を閉じずに次の接続要求があった場合、再利用する方法です。
接続オプションで PDO::ATTR_PERSISTENT => true を指定すると有効になります。
それぞれの接続方法を3回実行して掛かった時間の平均値は以下の通りです。
接続方法 | 時間 |
---|---|
暗号化無し | 7.6秒 |
SSL/TLS | 9分47秒 |
SSL/TLS + 持続的接続 | 2.4秒 |
SSL/TLS接続は接続コストがかなり掛かっていることがわかります。ただし、持続的接続(PDO::ATTR_PERSISTENT)オプションを有効にすることで、暗号化しない接続よりも高速になることもわかりました。
SSL/TLSでセキュアな接続とパフォーマンスの両方が必要な場合は、持続的接続を使用することも検討してください。ただし、以下のページにあるようにいくつかの注意点もあるため、十分な検証を行ってから導入するようにしてください。
まとめ
MySQL 5.7.6 以降では mysql_ssl_rsa_setup を使うことで簡単に自己認証局のCA鍵生成などを行うことができ、初回起動時に鍵生成が行われるのでデフォルトでSSL/TLSによる暗号化通信を利用することは可能ですが、設定としては十分ではありませんのでご自身のシステムのセキュリティ基準に応じて設定し直す必要があります。
また、アプリケーションからSSL/TLS接続を行う場合、接続にコストがかかるためパフォーマンスに問題が出る可能性もあるため、コネクションプーリングなどの導入も検討してください。
MySQL運用・管理の課題はプロが解決します!
データベースのパフォーマンスが悪い、ダウンタイムが気になる、複雑な運用に苦戦している…そんなMySQLの運用・管理に関するお悩みはありませんか?そんな方は、ぜひ当社のオープンソースDBサポートをご検討ください。
オラクル公式パートナーである当社の経験豊富なエンジニアが、MySQL / MariaDB / CassandraをはじめとしたOSSデータベースの導入から運用まで、あらゆる課題に対してハイレベルで迅速なサポートを提供します。システムのパフォーマンス改善や安定した運用を実現します。
初回のご相談・お見積りは無料です!
MySQL運用・管理にお悩みの方は、まずはお気軽にお問い合わせください。