本番環境向けMySQL/MariaDBセキュリティ強化:TLS、監査ログ、アクセス制御の実践

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

なぜ初期設定のデータベースは本番環境で通用しないのか

UbuntuやDebianにMySQLやMariaDBを新規インストールした直後の状態は、強固なセキュリティよりもセットアップの容易さが優先されています。apt installを実行した直後のデータベースサーバーは、オープンなインターフェースにバインドされていたり、内部VPCサブネット間で暗号化されていない接続を許可していたり、I/O負荷を抑えるためにクエリログが無効化されていたりすることが一般的です。

利便性を優先したこのようなデフォルト設定は、深刻な運用リスクをもたらします。

  • 平文トラフィックの盗聴: VPC内部、Kubernetesオーバーレイ(FlannelやCalicoなど)、またはクラウドサブネット内でパケットキャプチャを実行できる環境であれば、暗号化されていないSQLクエリを傍受できます。これにより、平文のパスワード、セッショントークン、顧客の個人情報(PII)が漏洩する危険があります。
  • 過剰な権限を持つサービスアカウント: アプリケーションがALL PRIVILEGESやワイルドカードホスト('app'@'%')で接続している場合、単一のエンドポイントで発生したSQLインジェクション脆弱性が、即座にデータベース全体のダンプやリモートサーバー侵害へとエスカレーションします。
  • 監査証跡の欠如: デフォルトのエラーログはデーモンのクラッシュや起動イベントを記録しますが、クエリ自体は無視します。深夜2時に誰かがテーブルを削除(DROP)したり、5万件のユーザーレコードをダンプしたりしても、標準のログでは実行者、リクエスト元のクライアントIP、実行されたクエリを特定できません。

データベース堅牢化(ハーデニング)の3本柱

データベースインスタンスの保護に、高価なエンタープライズ向けアドオンは不要です。代わりに、以下の3つの基本的なレイヤーに注力します。

1. トランスポート層セキュリティ(TLS/SSL)

TLSは、バックエンドアプリケーションとデータベースインスタンス間の通信を暗号化します。プライベートクラウドネットワーク上での盗聴を防止し、認証情報のやり取りを行う前にデータベースサーバーの身元を暗号学的に確認します。

2. 最小権限の原則(PoLP)

各アプリケーションおよびエンジニアには、その役割に必要な最小限の権限のみを付与します。例えば、レポート生成サービスには特定テーブルへのSELECT権限のみが必要です。バックエンドAPIにはデータ操作言語(DML)の権限(SELECT、INSERT、UPDATE、DELETE)が必要ですが、日常運用でスキーマ変更権限(DROP、ALTER)を持たせるべきではありません。

3. 継続的なクエリ監査

監査ロギングは、すべてのログイン試行、スキーマ変更(DDL)、機密クエリを変更不可能なレコードとして記録します。このテレメトリにより、インシデント対応が迅速化され、SOC 2 Type II、ISO 27001、HIPAAなどのコンプライアンス要件を満たすことができます。

ステップ・バイ・ステップ堅牢化ガイド

ステップ1:ネットワークの分離と詳細なユーザー権限設定

まずはリスニングインターフェースの制限から始めます。アプリケーションとデータベースが同じ仮想マシンを共有している場合は、厳格にローカルホスト(127.0.0.1)にバインドします。マルチティア構成の場合は、プライベートVPC IP(例:10.0.1.25)のみにバインドします。

主要な設定ファイル(MySQLの場合は/etc/mysql/mysql.conf.d/mysqld.cnf、MariaDBの場合は/etc/mysql/mariadb.conf.d/50-server.cnf)を更新します。

[mysqld]
# 内部プライベートインターフェースのみにバインド
bind-address = 10.0.1.25
port = 3306

# ローカルファイルのインジェクション脆弱性を防止
local_infile = 0

# テーブル上書き攻撃を防ぐためシンボリックリンクを無効化
skip_symbolic_links = 1

次に、管理者権限でデータベースプロンプトを開きます。

sudo mysql -u root -p

サービスアカウントにワイルドカードホスト('%')や短いパスワードを使用してはいけません。エントロピーの高い32文字の文字列を生成するには、ブラウザベースのパスワードジェネレーターtoolcraft.app/ja/tools/security/password-generatorを利用するのがおすすめです。ネットワーク経由でデータを送信することなく、クライアント側のみで安全にシークレットを生成できます。

以下のステートメントを実行して、IP制限された専用サービスアカウントを作成し、デフォルトのアカウントを削除します。

-- プライベートサーバーIPに直接紐付いたWebアプリケーション用ユーザーを作成
CREATE USER 'shop_app'@'10.0.1.50' IDENTIFIED BY 'v8K9#mQ2$pL7!zR4*wT1@jN6^bX3(yU9';

-- 本番テーブルに対するDML権限のみを厳格に付与
GRANT SELECT, INSERT, UPDATE, DELETE ON shop_production.* TO 'shop_app'@'10.0.1.50';

-- 分析ツール(Metabase、Looker)専用の読み取り専用ユーザーを作成
CREATE USER 'bi_readonly'@'10.0.1.60' IDENTIFIED BY 'cN4$tP9*kR2!yW7#mQ5@jL8^vX1(bZ3)';
GRANT SELECT ON shop_production.* TO 'bi_readonly'@'10.0.1.60';

-- 匿名アカウントとデフォルトのテスト用データベースを削除
DROP USER IF EXISTS ''@'localhost', ''@'127.0.0.1', ''@'%';
DROP DATABASE IF EXISTS test;

-- 権限テーブルを再読み込み
FLUSH PRIVILEGES;

ステップ2:TLS 1.3暗号化の強制

初期テストには自己署名証明書でも十分ですが、本番環境ではプライベートPKIで署名された専用の内部証明書、またはOpenSSLで生成した証明書が必要です。

2048ビットRSA鍵を使用して、専用の認証局(CA)とサーバー証明書ペアを生成します。

sudo mkdir -p /etc/mysql/ssl
cd /etc/mysql/ssl

# 1. 内部CAを作成
sudo openssl genrsa 2048 > ca-key.pem
sudo openssl req -new -x509 -nodes -days 1825 -key ca-key.pem -out ca.pem -subj "/CN=Internal-Database-CA"

# 2. サーバー秘密鍵とCSRを作成
sudo openssl req -newkey rsa:2048 -days 1825 -nodes -keyout server-key.pem -out server-req.pem -subj "/CN=db-primary.internal"
sudo openssl rsa -in server-key.pem -out server-key.pem
sudo openssl x509 -req -in server-req.pem -days 1825 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem

# 3. ファイルの所有権と権限を制限
sudo chown -R mysql:mysql /etc/mysql/ssl
sudo chmod 600 /etc/mysql/ssl/*-key.pem
sudo chmod 644 /etc/mysql/ssl/*.pem

これらの証明書を読み込み、古い暗号スイートを拒否するようにMySQL/MariaDBを設定します。

[mysqld]
ssl_ca = /etc/mysql/ssl/ca.pem
ssl_cert = /etc/mysql/ssl/server-cert.pem
ssl_key = /etc/mysql/ssl/server-key.pem

# 最新のTLSプロトコルバージョンのみを強制
tls_version = TLSv1.2,TLSv1.3

# 暗号化されていないクライアント接続をグローバルで拒否
require_secure_transport = ON

データベースサービスを再起動して設定を適用します。

sudo systemctl restart mysql # MariaDBの場合: sudo systemctl restart mariadb

アプリケーションアカウントが暗号化されたチャネル経由でのみ接続することを保証するため、ユーザーごとの要件を追加します。

ALTER USER 'shop_app'@'10.0.1.50' REQUIRE SSL;
FLUSH PRIVILEGES;

ステップ3:監査プラグインの有効化と設定

MariaDB Audit Plugin(server_audit)は、接続、切断、および実行されたクエリをログに記録します。MariaDBには標準で同梱されており、標準のMySQL環境でも動作させることができます。

MySQL/MariaDBのサーバー設定ファイルに監査設定を追加します。

[mysqld]
# 監査プラグインライブラリをロード
plugin_load_add = server_audit=server_audit.so

# ロギングを有効化
server_audit_logging = ON

# ログイン(CONNECT)、SQLクエリ(QUERY)、DDL変更(TABLE)を記録
server_audit_events = CONNECT,QUERY,TABLE

# 出力先ファイルとローテーション設定(1ファイル最大100MB、10世代保持)
server_audit_output_type = file
server_audit_file_path = /var/log/mysql/server_audit.log
server_audit_file_rotate_size = 104857600
server_audit_file_rotations = 10

# ノイズ低減のため内部メンテナンス用アカウントを除外
server_audit_excl_users = 'debian-sys-maint'

サービスを再起動するか、稼働中のサーバーでダウンタイムなしにプラグインを読み込みます。

INSTALL SONAME 'server_audit';
SET GLOBAL server_audit_logging = ON;
SET GLOBAL server_audit_events = 'CONNECT,QUERY,TABLE';
SET GLOBAL server_audit_file_path = '/var/log/mysql/server_audit.log';
SET GLOBAL server_audit_file_rotate_size = 104857600;
SET GLOBAL server_audit_file_rotations = 10;

ステップ4:検証とインシデントテスト

本番環境へデプロイする前に、すべてのセキュリティ境界を検証します。

1. TLSネゴシエーションの検証:

SHOW STATUS LIKE 'Ssl_cipher';
-- 期待される出力: TLS_AES_256_GCM_SHA384 または ECDHE-RSA-AES256-GCM-SHA384

2. 平文接続の拒否テスト:

# アプリケーションノードからSSLなしで接続を試行
mysql -u shop_app -p -h 10.0.1.25 --ssl-mode=DISABLED
# 期待される出力: ERROR 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON.

3. 監査ログ出力の確認:

sudo tail -n 5 /var/log/mysql/server_audit.log

タイムスタンプ、ホスト名、ユーザー名、送信元IP、接続ID、クエリペイロードが記録された構造化CSV行が表示されるはずです。

本番稼働前チェックリスト

セキュリティの堅牢化は、継続的な運用規律です。本番クライアントアプリケーションをクラスタに向ける前に、以下の項目を確認してください。

  1. ネットワーク境界: ポート3306がプライベートIPにのみバインドされ、ファイアウォールルール(ufwやAWSセキュリティグループなど)によってパブリックインターネットから遮断されていること。
  2. ワイルドカードの完全排除: mysql.userテーブルに'%'ホスト定義のアカウントや空のユーザー名が存在しないこと。
  3. 転送中暗号化の必須化: require_secure_transport = ONが有効であり、コネクションプールに有効な証明書を伴うsslmode=verify-fullまたはsslmode=verify-caが設定されていること。
  4. ログ転送とストレージ: ローカルディスクの容量枯渇を防ぐため、監査ログが専用ディスクボリュームに配置されているか、ログ収集ツール(Vector、Filebeat、Grafana Lokiなど)へストリーミングされていること。

これら3つのレイヤーを適用することで、データベースはネットワーク越しのデータを保護し、権限昇格を防ぎ、監査コンプライアンスに必要なすべてのクエリを記録できるようになります。

Share: