こんにちは、かつコーチです。
「アプリのDB接続用ユーザーに、うっかりrootを使い続けていませんか」
開発中はつい楽なのでrootユーザーで全部済ませてしまいがちですが、これは本番運用ではかなり危険な状態です。
もしアプリにSQLインジェクションの脆弱性があった場合、root権限のままだと被害がテーブルの中身だけでなく、ユーザー管理やサーバー設定にまで及んでしまいます。
この記事では、MySQLのユーザー権限管理の基本であるGRANT(権限を付与するコマンド)とREVOKE(権限を剥奪するコマンド)の使い方を解説します。
「必要な権限だけを、必要なユーザーにだけ与える」という考え方は、セキュリティの世界で最小権限の原則(ユーザーには業務上必要最低限の権限だけを与えるという考え方)と呼ばれる基本中の基本です。
MySQLの権限管理の仕組み
ユーザーとホストの組み合わせで権限が決まる
MySQLでは、ユーザーは単なる「ユーザー名」ではなく、「ユーザー名 + 接続元ホスト」の組み合わせで識別されます。
-- 'app_user'@'localhost' と 'app_user'@'%' は別ユーザー扱い
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'StrongPassw0rd!';
CREATE USER 'app_user'@'%' IDENTIFIED BY 'StrongPassw0rd!';
localhostはそのサーバー上からの接続のみ許可、%はどこからでも接続を許可という意味です。
同じユーザー名でも接続元が違えば別ユーザーとして扱われるため、権限も個別に設定する必要があります。
権限の階層(グローバル・データベース・テーブル・カラム)
MySQLの権限は、大きく分けて4段階の階層で管理できます。
| 階層 | 指定方法 | 用途 |
|---|---|---|
| グローバル | *.* | サーバー全体に対する権限(管理用ユーザー向け) |
| データベース単位 | dbname.* | 特定のDBだけへのアクセス許可 |
| テーブル単位 | dbname.tablename | 特定のテーブルだけへのアクセス許可 |
| カラム単位 | SELECT(col1, col2) | 特定のカラムだけの読み取り許可 |
アプリケーション用のユーザーは、基本的に「データベース単位」で権限を絞るのが実務上の標準的な運用です。
GRANTで権限を付与する
基本の書き方
GRANT文の基本構文は次の通りです。
GRANT 権限リスト ON データベース.テーブル TO 'ユーザー名'@'ホスト';
例えば、アプリケーション用ユーザーにmyappデータベースへのCRUD操作(作成・読み取り・更新・削除)権限だけを与えるには、次のように書きます。
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_user'@'%';
このユーザーは、myappデータベース内のデータ操作はできますが、テーブルの作成・削除(CREATE・DROP)やユーザー管理はできません。
権限の種類を目的別に把握する
よく使う権限は次の通りです。
| 権限 | 意味 | 想定ユーザー |
|---|---|---|
SELECT | データの読み取り | 全アプリユーザー |
INSERT | データの追加 | 書き込みを行うアプリユーザー |
UPDATE | データの更新 | 同上 |
DELETE | データの削除 | 同上 |
CREATE | テーブル・DBの作成 | マイグレーション実行ユーザー |
ALTER | テーブル構造の変更 | 同上 |
DROP | テーブル・DBの削除 | 開発環境の管理者のみ |
ALL PRIVILEGES | すべての権限 | 管理者ユーザーのみ |
「読み取り専用のレポート用ユーザー」を作りたい場合は、SELECT権限だけを与えます。
CREATE USER 'report_user'@'%' IDENTIFIED BY 'ReportPass123!';
GRANT SELECT ON myapp.* TO 'report_user'@'%';
FLUSH PRIVILEGES;
FLUSH PRIVILEGESは権限テーブルの変更をすぐに反映させるコマンドです。
MySQL 8.0以降はGRANT実行時に自動で反映される場合が多いですが、権限テーブルを直接編集した場合などは明示的に実行しておくと安心です。
つまずきやすい設定・注意点
権限設定でよくあるつまずきが、存在しないユーザーへのGRANTです。
ERROR 1133 (42000): Can't find any matching row in the user table
これはMySQL 5.7以前でよく見たエラーで、GRANT文がユーザー作成を兼ねていた古い仕様に依存したコードをMySQL 8.0で実行すると発生します。
MySQL 8.0ではGRANTとユーザー作成が分離されているため、必ずCREATE USERを先に実行してからGRANTを行う必要があります。
-- ❌ Before:MySQL 8.0ではエラーになる書き方(5.7以前の癖が残っている)
GRANT SELECT ON myapp.* TO 'new_user'@'%' IDENTIFIED BY 'pass123';
-- ✅ After:CREATE USERとGRANTを分離する
CREATE USER 'new_user'@'%' IDENTIFIED BY 'pass123';
GRANT SELECT ON myapp.* TO 'new_user'@'%';
筆者もマイグレーションスクリプトを古いMySQL 5.7時代のまま流用してこのエラーに遭遇したことがあります。
MySQL 8.0系のマニュアルやチュートリアルを参照する際は、バージョンが古い情報でないか必ず確認するようにしています。
REVOKEで権限を剥奪する
基本の書き方
不要になった権限を取り消すにはREVOKE文を使います。
REVOKE 権限リスト ON データベース.テーブル FROM 'ユーザー名'@'ホスト';
例えば、先ほどのapp_userからDELETE権限だけを取り消したい場合は次のようにします。
REVOKE DELETE ON myapp.* FROM 'app_user'@'%';
論理削除(deleted_atカラムで削除フラグを立てる設計)を採用しているアプリでは、そもそもアプリ用ユーザーに物理削除のDELETE権限を与えない、という運用も有効です。
権限の確認方法
現在どの権限が付与されているかはSHOW GRANTSで確認できます。
SHOW GRANTS FOR 'app_user'@'%';
+---------------------------------------------------------------+
| Grants for app_user@% |
+---------------------------------------------------------------+
| GRANT USAGE ON *.* TO `app_user`@`%` |
| GRANT SELECT, INSERT, UPDATE ON `myapp`.* TO `app_user`@`%` |
+---------------------------------------------------------------+
REVOKEを実行した後は、必ずSHOW GRANTSで意図通りに権限が変わっているか確認する習慣をつけましょう。
権限まわりの操作は、確認を怠ると「権限が足りずアプリが動かない」「逆に権限が残ったまま気づかない」といったトラブルにつながりやすい部分です。
応用・一歩先の使い方(中級読者向けブリッジ)
ロールを使った権限管理(MySQL 8.0の新機能)
MySQL 8.0からはロール(複数の権限をまとめた「権限のグループ」)という仕組みが使えるようになりました。
ユーザーが増えるたびに同じGRANT文を繰り返すのは非効率なので、ロールを使うと管理がぐっと楽になります。
-- ロールを作成し、権限をまとめる
CREATE ROLE 'app_readonly';
GRANT SELECT ON myapp.* TO 'app_readonly';
-- ユーザーにロールを割り当てる
CREATE USER 'analyst1'@'%' IDENTIFIED BY 'Pass1234!';
GRANT 'app_readonly' TO 'analyst1'@'%';
-- ロールを有効化(デフォルトで有効にする場合)
SET DEFAULT ROLE 'app_readonly' TO 'analyst1'@'%';
分析担当者が増えるたびに同じGRANT SELECTを書く必要がなくなり、「このロールを割り当てる」だけで済むのが利点です。
チームの人数が増えてくるフェーズでは、ロールベースの権限管理に切り替えることを検討してみてください。
まとめ
この記事のポイント
- MySQLの権限は「ユーザー名 + 接続元ホスト」の組み合わせで識別される
- アプリ用ユーザーには
rootを使わず、必要な権限だけをGRANTで絞って与える - MySQL 8.0では
CREATE USERとGRANTが分離されているため、古い書き方のまま流用するとエラーになる REVOKEで不要な権限を剥奪し、SHOW GRANTSで必ず結果を確認する- ユーザー数が増えてきたら、MySQL 8.0の「ロール」機能で管理をまとめると効率的
次に読むべき記事
- SQLインジェクション対策とプレースホルダの使い方
- よくあるMySQLエラーまとめ(Access denied等)
タグ: MySQL, 中級者向け, セキュリティ