MySQL mà không có SSL? Bạn đang chơi đùa với lửa!
デフォルトでは、MySQLはデータをプレーンテキストで送信します。アプリケーションとデータベースが同じサーバー上にあり、ソケット経由で接続されている場合は問題ありません。しかし、マイクロサービスを運用したり、クラウド間をインターネット経由で接続したりする場合、これは非常に大きな脆弱性となります。Wiresharkのようなツールを使えば、わずか5分で、管理者のパスワードから顧客のクレジットカード情報まで、すべてのSQLクエリを簡単に傍受できてしまいます。
以前、システムを50ノードに拡張した際に、あるトラブルに対応したことがあります。内部ネットワークだからと暗号化を怠っていたところ、定期的なセキュリティ監査で決済時のペイロードが完全に露出していることが判明しました。これは私にとって手痛い教訓となり、それ以来、データベースの初期構築時にはSSL/TLSの実装を最優先事項としています。
知っておくべき3つの接続セキュリティレベル
プロジェクトの要件に応じて、以下の3つのレベルから選択できます:
- 暗号化なし接続(Unencrypted): 速度は最速ですが、リスクは最大です。
localhost経由の内部接続のみに使用すべきです。 - 標準SSL(One-way TLS): サーバーが証明書を提示し、クライアントがそれを検証します。データは暗号化され、盗聴を防ぎますが、サーバーはクライアントが誰であるかを把握していません。
- クライアント証明書認証(Two-way TLS/mTLS): 最も「ハードコア」なレベルです。サーバーとクライアントの両方が、同じCAによって署名された有効な証明書を提示する必要があります。ハッカーがパスワードを手に入れたとしても、端末内に秘密鍵ファイルがなければ、手出しすることはできません。
各手法のクイック比較:
| 項目 | 暗号化なし | 標準SSL | クライアント証明書 (mTLS) |
|---|---|---|---|
| データの暗号化 | なし | あり | あり |
| サーバー認証 | なし | あり | あり |
| クライアント認証 | パスワードのみ | パスワードのみ | 証明書 + パスワード |
| 安全性 | 低 | 中 | 最大 |
なぜ大規模システムでmTLSが選ばれるのか?
実際、パスワードは設定ファイルや環境変数から漏洩しがちです。クライアント証明書を導入することで、物理的なセキュリティレイヤーが追加されます。たとえログイン情報が盗まれたとしても、攻撃者はアプリケーションサーバーに安全に保管されている鍵ファイルを必要とします。これはデータベースを隔離し、特定のノードのみにアクセスを許可するための最も効果的な方法です。
実装のステップバイステップ
ステップ1:内部認証局(CA)の構築
内部接続の場合、Let’s Encryptを使用する代わりに、独自のCAを作成して管理することをお勧めします。OpenSSLを使用して以下のキーセットを作成しましょう。
# CAの作成(秘密鍵と自己署名証明書)
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca.pem -subj "/CN=MySQL_Internal_CA"
# サーバー用の秘密鍵と証明書の作成
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem -subj "/CN=mysql-server"
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
# クライアント用の秘密鍵と証明書の作成
openssl req -newkey rsa:2048 -days 3650 -nodes -keyout client-key.pem -out client-req.pem -subj "/CN=mysql-client-app-1"
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem -set_serial 02 -out client-cert.pem
その結果、5つのファイルが生成されます。ca-key.pemは安全な場所(できればオフライン)に厳重に保管してください。
ステップ2:MySQLサーバー側の設定
ca.pem、server-cert.pem、server-key.pem ファイルを /etc/mysql/ssl/ ディレクトリにコピーします。忘れずに chown mysql:mysql で所有権を設定してください。その後、設定ファイル my.cnf を更新します。
[mysqld]
ssl-ca=/etc/mysql/ssl/ca.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
# 暗号化されていない接続を完全に遮断する
require_secure_transport = ON
変更を適用するためにサービスを再起動します:sudo systemctl restart mysql。
ステップ3:X509認証を要求するユーザーの作成
ここが重要なステップです。MySQLが証明書をチェックしなければログインを許可しないユーザーを作成します。
CREATE USER 'app_user'@'%' IDENTIFIED BY 'super_secure_password_123';
GRANT ALL PRIVILEGES ON prod_db.* TO 'app_user'@'%' REQUIRE X509;
FLUSH PRIVILEGES;
REQUIRE X509 キーワードにより、たとえハッカーがパスワードを知っていたとしても、発行した証明書がなければログインできないことが保証されます。
ステップ4:クライアントからの接続
設定を確認するためにCLIでクイックテストを行います。
mysql -u app_user -p -h db.example.com \
--ssl-ca=ca.pem \
--ssl-cert=client-cert.pem \
--ssl-key=client-key.pem
Pythonによる接続例:
import mysql.connector
conn = mysql.connector.connect(
user='app_user',
password='super_secure_password_123',
host='10.0.0.5',
database='prod_db',
ssl_ca='certs/ca.pem',
ssl_cert='certs/client-cert.pem',
ssl_key='certs/client-key.pem'
)
cursor = conn.cursor()
cursor.execute("SHOW STATUS LIKE 'Ssl_cipher'")
print(f"接続中のアルゴリズム: {cursor.fetchone()[1]}")
conn.close()
運用に関するいくつかの「手痛い」注意点
SSLを数年間運用してきた中で、3つの重要なポイントをまとめました。
- パフォーマンス(CPUオーバーヘッド): SSLハンドシェイクはかなりのリソースを消費します。ベンチマークテストでは、新しい接続を頻繁に作成すると、スループットが1000 req/sから600 req/sに低下することがあります。SSL接続を維持するために、Connection Pool(ProxySQLなど)を使用してください。
- 証明書の期限切れに注意: 最悪なのは、証明書の期限切れで午前2時にシステムがダウンすることです。監視を設定するか、30日前に自動的に警告を出すスクリプトを使用しましょう。
- CAチェーンのエラー:
self signed certificate in certificate chainというエラーが発生した場合、90%の確率でクライアント側のca.pemファイルがサーバーの署名に使用されたCAと一致していません。チェックサムを確認してください。
SSL/TLSの実装は最初は少し面倒に感じるかもしれません。しかし、ハッカーの好奇の目からデータが「露出」していないと分かれば、より安眠できるはずです。

