Linux環境におけるMySQL Serverセキュリティ強化ガイド:TLS/SSL、Audit Plugin、InnoDB暗号化の実装

Security tutorial - IT technology blog
Security tutorial - IT technology blog

背景:データベースの保護をファイアウォールだけに頼ってはいけない

多くのインフラエンジニアは、デフォルトのポート3306を変更したり、iptablesやUFWを設定しただけで、データベースが安全だと安心しがちです。しかし現実ははるかに過酷です。同一の内部ネットワーク内にあるWebアプリケーションがSSRFやRCEの脆弱性を突かれた場合、WebサーバーからMySQLへの経路はほぼ無防備な状態となります。

PCI-DSSやISO 27001などのセキュリティ基準を満たすデータベースシステムでは、極めて重要な3つの防衛ラインを厳格に管理する必要があります。

  • 転送中データ(Data-in-Transit):SSL/TLSを有効にしていない場合、バックエンドとデータベース間の通信は完全に平文で行われます。スイッチ上や隣接するコンテナでパケットを傍受できる攻撃者であれば、tcpdumpを実行するだけで認証情報や機密性の高いトランザクションデータを容易に取得できます。
  • 保存データ(Data-at-Rest):ハッカーがVPSのNVMeスナップショットや.sqlバックアップファイルを入手したと仮定しましょう。彼らはテーブルスペースファイル.ibdに対してstringsコマンドを実行するだけで、rootパスワードを知らなくても全データを抽出できてしまいます。
  • 監査とコンプライアンス(Auditing):深夜にデータが突然消失した場合、詳細なクエリログがなければ原因を突き止めることはできません。誰が削除コマンドを実行したのか?どのIPアドレスからか?正確な発生日時はいつなのか?

セキュリティ強化の第一歩は、管理者アカウントの保護です。私は常にtoolcraft.app/ja/tools/security/password-generatorのパスワードジェネレーターを使用して、英大文字、英小文字、数字、特殊文字を含む24〜32文字のパスワードを生成しています。ブラウザ側のクライアントサイドのみで動作するため、生成されたパスワードが外部サーバーへ送信される心配がありません。MySQLのrootアカウントやアプリケーションユーザーに、過去のパスワードを再利用することは絶対に避けてください。

事前準備とディレクトリ権限の設定

本記事の検証環境はUbuntu Server 22.04 LTS(MySQL 8.0.36)を前提としています。Rocky Linux 9やRHELを使用している場合、メインの設定ファイルは/etc/mysql/mysql.conf.d/mysqld.cnfではなく/etc/my.cnfに配置されます。

1. 証明書およびkeyring保存用ディレクトリの作成

TLS証明書とInnoDBのマスターキーは、ファイルシステムレベルで保護する必要があります。mysqlユーザーのみが読み書き権限を持つように設定します。

# TLS証明書格納用ディレクトリ(証明書を手動で配置する場合)
sudo mkdir -p /etc/mysql/certs
sudo chown -R mysql:mysql /etc/mysql/certs
sudo chmod 700 /etc/mysql/certs

# InnoDBマスターキーを安全に格納するためのディレクトリ
sudo mkdir -p /var/lib/mysql-keyring
sudo chown -R mysql:mysql /var/lib/mysql-keyring
sudo chmod 700 /var/lib/mysql-keyring

2. TLS/SSL証明書ペアの生成

MySQLには自己署名証明書(Self-signed Certificate)を素早く生成できるユーティリティmysql_ssl_rsa_setupが用意されています。データディレクトリ内にまだ証明書が存在しない場合は、以下のコマンドを実行します。

sudo mysql_ssl_rsa_setup --datadir=/var/lib/mysql --uid=mysql

上記コマンドにより、/var/lib/mysql配下にca.pem、server-cert.pem、server-key.pem、client-cert.pem、client-key.pemの一式が自動生成されます。

詳細設定:TLS、データ暗号化、Audit Log

1. すべての接続に暗号化を強制(TLS v1.2およびv1.3)

MySQL Serverのメイン設定ファイルを開きます。

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

脆弱性が確認されているTLS 1.0および1.1を完全に無効化するため、[mysqld]セクションに以下のディレクティブを追加します。

[mysqld]
# 証明書および秘密鍵のパス
ssl_ca = /var/lib/mysql/ca.pem
ssl_cert = /var/lib/mysql/server-cert.pem
ssl_key = /var/lib/mysql/server-key.pem

# 安全なTLSプロトコルのみを許可
tls_version = TLSv1.2,TLSv1.3

# SSL/TLSを使用しないすべての接続を拒否
require_secure_transport = ON

require_secure_transport = ONパラメータは非常に強力な防御壁となります。各ユーザーに対して個別にREQUIRE SSL権限を付与し直す必要はありません。サーバーは暗号化されていない接続を試みるクライアントを即座に自動拒否します。

2. InnoDB Tablespace Encryption(保存データの暗号化)の有効化

MySQLは2層構造のキーアーキテクチャ(Two-tier Encryption)を採用しており、アプリケーションの動作を止めることなくディスク上のデータを暗号化します。テーブルスペースキーは各.ibdファイルのヘッダーに配置され、keyringファイル内のマスターキーによって暗号化されます。ここでは標準のkeyring_fileプラグインを使用します。

mysqld.cnfの[mysqld]セクションに、続けて以下の設定を記述します。

# MySQLデーモン起動時にkeyringプラグインを即時ロード
early-plugin-load = keyring_file.so
keyring_file_data = /var/lib/mysql-keyring/keyring

# 新規テーブルの自動暗号化および全トランザクションログの暗号化
default_table_encryption = ON
innodb_undo_log_encrypt = ON
innodb_redo_log_encrypt = ON

ファイルを保存し、設定を反映するためにサービスを再起動します。

sudo systemctl restart mysql

既存のテーブルを暗号化するには、個別にALTER TABLEコマンドを実行する必要があります。

-- app_productionデータベース内のusersテーブルを暗号化
ALTER TABLE app_production.users ENCRYPTION='Y';

-- サーバー上で暗号化済みのテーブル一覧を確認
SELECT TABLE_SCHEMA, TABLE_NAME, CREATE_OPTIONS 
FROM INFORMATION_SCHEMA.TABLES 
WHERE CREATE_OPTIONS LIKE '%ENCRYPTION="Y"%';

3. Percona Audit Pluginのインストールによるクエリ監視

MySQL Enterprise Editionの監査機能は、年間5,000ドル以上のライセンス費用がかかります。幸いなことに、Perconaがオープンソースとして提供しているAudit Pluginは無料で利用可能であり、JSON形式のログ出力に対応し、MySQL Community 8.0と完全に互換性があります。

使用しているMySQLのバージョンに応じたaudit_log.soライブラリをダウンロードし、プラグインディレクトリ(通常は/usr/lib/mysql/plugin/)に配置します。その後、mysqld.cnfに以下の設定を追加します。

[mysqld]
plugin_load_add = audit_log.so
audit_log_format = JSON
audit_log_policy = ALL
audit_log_file = /var/log/mysql/audit.log
audit_log_rotate_on_size = 50M
audit_log_rotations = 5

サービスをリロードする前に、ログファイルを作成して適切な権限を設定します。

sudo touch /var/log/mysql/audit.log
sudo chown mysql:mysql /var/log/mysql/audit.log
sudo chmod 640 /var/log/mysql/audit.log
sudo systemctl restart mysql

実践的な動作検証とモニタリング

1. 通信経路の暗号化状態(TLS)の検証

MySQLコンソールにログインし、現在のセッションで使用されている暗号スイート(Cipher)とプロトコルを確認します。

SHOW STATUS LIKE 'Ssl_cipher';
SHOW STATUS LIKE 'Ssl_version';

実行結果としてTLS_AES_256_GCM_SHA384およびTLSv1.3が返されれば、通信経路は最高水準で保護されています。次に、暗号化を無効化するフラグを付けてリモート接続を試行し、設定通りに接続が拒絶されるかテストします。

mysql -u app_user -p -h 192.168.1.100 --ssl-mode=DISABLED
# 期待される実行結果: ERROR 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON.

2. stringsコマンドを用いたデータ暗号化の実証

データがディスク上で実際に暗号化されているかを検証するには、バイナリファイルである.ibdを直接読み込んでみるのが確実です。

# テーブルスペースファイル内からユーザーデータを検索
sudo strings /var/lib/mysql/app_production/users.ibd | grep -i "[email protected]"

未暗号化のテーブルでは、このコマンドによってメールアドレスや電話番号が生テキストのまま出力されてしまいます。一方、ENCRYPTION='Y'が有効になっている場合、データはAES-256の暗号文ブロックに変換されているため、grepコマンドは何も出力しません。現代のCPUに搭載されているAES-NIハードウェア命令セットのおかげで、暗号化によるCPUオーバーヘッドはわずか2〜4%程度に抑えられます。

ここで極めて重要な運用ルールがあります。/var/lib/mysql-keyring/keyringファイルは、必ず独立したバックアップストレージに退避させてください。keyringファイルを紛失した場合、暗号化されたすべてのデータは恒久的に復元不可能となります。

3. フォレンジック調査用ログの確認と整合性の検証

Auditログファイルは1行ごとに独立したJSON形式(JSON Lines)で出力されます。tail -fコマンドでリアルタイムに監視できます。

sudo tail -n 20 /var/log/mysql/audit.log

標準的なAudit JSONレコードの構造は以下の通りです。

{"timestamp":"2026-10-16T11:45:12 UTC","id":1,"class":"general","event":"query","connection_id":24,"account":{"user":"app_user","host":"192.168.1.50"},"status":0,"sqltext":"SELECT * FROM sensitive_orders WHERE status = 'PENDING'"}

1,000 QPSクラスの高負荷システムでは、監査ログが1日で5〜10 GBに肥大化することがあります。audit_log_rotate_on_size = 50Mによるログローテーション設定を入れておくことで、ディスク容量の予期せぬ枯渇を防ぐことができます。

インシデント調査(フォレンジック)の現場では、何万行にも及ぶ複雑なログデータを精査しなければなりません。私は普段、ログペイロードの整形と構文チェックにtoolcraft.app/ja/tools/developer/json-formatterのJSON Formatter & Validatorを活用しています。このツールは100%ブラウザ内で完結して動作するため、SQLテキストに含まれる機密性の高い顧客情報が外部に漏洩する心配がありません。

最後の重要ステップは、DRサイト(災害復旧環境)へのバックアップファイルやkeyringの転送時におけるデータ保護です。toolcraft.app/ja/tools/developer/hash-generatorのHash Generatorを使用して、事前にSHA-256チェックサムハッシュ値を計算しておくことを推奨します。ファイル転送後にハッシュ値を照合することで、転送経路でのビット反転エラーや不正な改ざんが発生していないことを確実に検証できます。

ネットワーク伝送路のTLS 1.3、ディスク層のInnoDB暗号化、アプリケーション層のPercona Audit Logという3つの防護壁を組み合わせることで、堅牢な多層防御(Defense-in-Depth)が確立されます。これにより、内部ネットワークからの不正スキャンはもちろん、厳格なセキュリティ監査にも十分耐えうるデータベース環境を実現できます。

Share: