Linux環境におけるMySQL Serverの堅牢化:実践的なセキュリティ強化と権限分離の手順

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

クイックスタート:5分で完了するMySQL初期セキュリティチェックリスト

UbuntuやRocky LinuxにMySQLノードを構築し、バックエンドと接続する準備が整った段階であれば、開発チームへのアカウント配布やステージング環境へのデプロイを行う前に、まずは以下の基本3ステップを必ず実行してください。

1. デフォルトのセキュリティスクリプトを実行する

sudo mysql_secure_installation

対話型ウィザード形式で進みます。以下の設定を推奨します:

  • パスワード強度検証コンポーネント(VALIDATE PASSWORD)をMEDIUMまたはSTRONGで有効化する。
  • Anonymous User(インストール時に作成される匿名ユーザー)をすべて削除する。
  • rootアカウントのリモートログインを無効化する(Disallow root login remotely)。
  • デフォルトのtestデータベースを削除し、権限テーブル(privilege tables)をリロードする。

2. プライベートインターフェースのみでリッスンするよう制限する

一部のディストリビューションでは、初期状態でMySQLがすべてのネットワークインターフェース(0.0.0.0)をリッスンしている場合があります。設定ファイル /etc/mysql/mysql.conf.d/mysqld.cnf(RHEL/Rocky系の場合は /etc/my.cnf)を開き、bind-address の設定値を変更します:

[mysqld]
# 同一ホスト(localhost)上のアプリのみ接続を許可する場合:
bind-address = 127.0.0.1

# または、同一VPC内のプライベートネットワークから接続させる場合は、該当インターフェースのPrivate IPを指定:
# bind-address = 10.0.1.15

ファイルを保存後、サービスを再起動して設定を反映させます:

sudo systemctl restart mysql

3. サーバーのファイアウォール層でポート3306を制限する

インフラの構成変更時などにアプリケーション側の設定ミスが生じるリスクに備え、OSのファイアウォールでも確実に防御ラインを構築しておきます。

# サーバーから締め出されないよう、ufwを有効化する前に必ずSSHポートを許可しておくこと!
sudo ufw allow 22/tcp
sudo ufw default deny incoming

# アプリケーションサーバー群のサブネット(例: 10.0.2.0/24)からのみポート3306へのアクセスを許可
sudo ufw allow from 10.0.2.0/24 to any port 3306 proto tcp
sudo ufw reload

詳細解説:本番データベースを守る3層の防御アプローチ

基本的な初期設定を終えたら、さらに強固な技術的防壁を築きます。核となる設計思想は、「万が一Webサーバーが侵害されても、データベース層で攻撃者の足止めができること」です。

第1層:ストレージエンジンの封鎖とデータ流出(Data Exfiltration)の防止

SQLインジェクションの脆弱性を突かれ、/etc/passwd などのシステムファイルを読み取られたり、INTO OUTFILE を利用してWebシェルを配置されたりする攻撃手法は非常に一般的です。この攻撃ルートはMySQLの設定レベルで無力化できます。

設定ファイルの [mysqld] セクションに以下のパラメータを追加します:

[mysqld]
# クライアント側からデータベースへの勝手なローカルファイル送信を禁止
local_infile = 0

# インポート/エクスポートが許可されるディレクトリを固定、または完全に禁止する場合は空を設定
secure_file_priv = /var/lib/mysql-files

# datadir外部のファイルへの不正アクセスを防ぐためシンボリックリンクを無効化
skip_symbolic_links = 1

secure_file_priv を明示的に制限しておくことで、攻撃者が SELECT ... INTO OUTFILE を悪用できたとしても、/var/www/html などの公開ディレクトリにWebシェルを書き込んで実行させることは不可能になります。

第2層:アカウントの分離と最小権限の原則(Least Privilege)の適用

現場で最も頻発するミスは、バックエンド接続用ユーザーに GRANT ALL PRIVILEGES を付与してしまうケースです。この状態でAPIがSQLiの被害に遭うと、一瞬にしてスキーマ全体が DROP されてしまう危険性があります。

適切な運用方針として、最低でも役割(Role)ごとに2種類のアカウントを分離して管理します:

-- 1. バックエンド(NodeJS、Spring、Laravelなど)のランタイム用アカウント
CREATE USER 'app_runtime'@'10.0.2.%' IDENTIFIED BY 'Tr0ng-P@ss#Prod2026';

-- データ操作(DML)のみに権限を制限。DROP、ALTER、CREATE、TRUNCATEなどは一切不許可
GRANT SELECT, INSERT, UPDATE, DELETE ON production_db.* TO 'app_runtime'@'10.0.2.%';

-- 2. CI/CDパイプラインでスキーママイグレーションを実行するため専用のアカウント
CREATE USER 'app_migrator'@'10.0.2.50' IDENTIFIED BY 'Migrate_Secr3t#2026';
GRANT CREATE, ALTER, DROP, INDEX, REFERENCES ON production_db.* TO 'app_migrator'@'10.0.2.50';

FLUSH PRIVILEGES;

このように分離することで、侵害時の被害を最小限に抑えられます。アプリケーションのランタイムユーザーはレコードの読み書きのみを担当し、テーブル構造を変更する権限を一切持たない状態を維持できます。

第3層:ユーザー単位のリソース制限の設定

過度なスクレイピングを行うボットや、コネクションリークを伴うループ処理の不具合により、MySQLのCPUリソースが枯渇することがあります。あらかじめアカウント単位でクォータを設定しておきましょう:

ALTER USER 'app_runtime'@'10.0.2.%' WITH
  MAX_QUERIES_PER_HOUR 500000
  MAX_UPDATES_PER_HOUR 100000
  MAX_CONNECTIONS_PER_HOUR 10000
  MAX_USER_CONNECTIONS 150;

MAX_USER_CONNECTIONS = 150 の値は、グローバル設定の max_connections(サーバーのRAM容量に応じて500〜1000程度に設定することが一般的)や、アプリ側のコネクションプール設定と整合性を取って調整してください。

監査ログとコマンド履歴の管理

スロークエリはアプリケーションのパフォーマンスを低下させますが、深夜の不正クエリは致命的なデータ損失を引き起こします。インシデント調査に備え、アクセス履歴を確実に記録しておく必要があります。

1. 不審なログイン試行の監視

MySQL Community Editionにおいてブルートフォース攻撃を追跡する最も手軽な方法は、エラーログの出力レベルを引き上げることです:

[mysqld]
log_error = /var/log/mysql/error.log
log_error_verbosity = 3

詳細レベル 3(Verbose)を指定すると、警告情報に加えて接続失敗時のログ、送信元IPアドレス、拒絶理由(Access Denied)がエラーログへ詳細に記録されるようになります。

2. MySQLクライアントのコマンド履歴の無効化

CLI上でコマンドを実行すると、MySQLクライアントはデフォルトで入力履歴をすべて ~/.mysql_history に保存します。ここにはユーザー作成時の平文パスワードなどがそのまま平文で残ってしまいます。

このリスクを排除するため、履歴ファイルを /dev/null にシンボリックリンクします:

# 既存ファイルを削除し、永続的に /dev/null へリダイレクト
rm -f ~/.mysql_history
ln -s /dev/null ~/.mysql_history

# システム全体に適用する環境変数を設定
echo "export MYSQL_HISTFILE=/dev/null" | sudo tee -a /etc/profile.d/mysql_history.sh
source /etc/profile.d/mysql_history.sh

実務における運用上の注意点

データベース運用において、定期的にチェックすべき重要項目を挙げます:

  • SUPER権限やFILE権限を安易に付与しない:これらの権限を持つユーザーは、システム全体のグローバル変数を書き換えたり、ホストサーバー上のファイルを操作したりすることが可能になります。ローカルの root アカウントのみに限定してください。
  • TLSによる通信暗号化の強制:プライベートなVPC内であっても、同一ネットワーク内の別ノードが侵害された場合、機密データが盗聴されるリスクが存在します。[mysqld] ブロックで require_secure_transport = ON を設定し、SSL/TLSを使用しない接続をすべて拒否するように構成します。
  • ログローテーション(logrotate)の構成確認:/etc/logrotate.d/mysql-server の設定を必ず確認してください。エラーログの圧縮と定期的なクリーンアップを怠ると、肥大化したログがルートパーティション(/)を圧迫し、ディスクフルによってMySQLが突然強制停止する原因となります。
Share: