MySQLでユーザーに権限を与える手順とは、CREATE USER でアカウントを作り、GRANT で権限を付け、SHOW GRANTS で確認する3ステップです。 MySQL 8.0以降は GRANT だけでユーザーを作ることはできず、必ず CREATE USER を先に実行します。

この記事では、作成 → 付与 → 確認 → 取り消し → 変更 → 削除の流れを、そのまま貼って流せるSQLと、MySQL 8.0.46で実際に実行した結果つきでまとめました。ERROR 1410・1045・1142・3118 などのエラー全文の再現、localhost と % の違いが原因で起きるログイン失敗、ロールの「付けたのに効かない」問題、用途別の権限テンプレート(読み取り専用・アプリ用・mysqldump用・レプリケーション用)まで、1ページで判断できるように整理しています。

MySQLユーザー・権限操作の早見表

やりたいこと SQL 確認方法
ユーザーを作る CREATE USER 'app'@'localhost' IDENTIFIED BY 'パスワード'; SELECT user, host FROM mysql.user;
既にあってもエラーにしない CREATE USER IF NOT EXISTS ... SHOW WARNINGS;(Note 3163)
DB全体に権限を付ける GRANT SELECT, INSERT ON shop.* TO 'app'@'localhost'; SHOW GRANTS FOR 'app'@'localhost';
テーブル単位で付ける GRANT SELECT ON shop.orders TO ...; 同上
カラム単位で付ける GRANT SELECT (id, item) ON shop.orders TO ...; 同上
権限を取り消す REVOKE DELETE ON shop.* FROM 'app'@'localhost'; 同上
全権限を取り消す REVOKE ALL PRIVILEGES, GRANT OPTION FROM ...; USAGE だけ残る
パスワードを変える ALTER USER 'app'@'localhost' IDENTIFIED BY '新パスワード'; ログインで確認
自分のパスワードを変える ALTER USER USER() IDENTIFIED BY '新パスワード'; 同上
パスワードを期限切れにする ALTER USER ... PASSWORD EXPIRE; ERROR 1820 で変更を促される
有効期限を設定する ALTER USER ... PASSWORD EXPIRE INTERVAL 90 DAY; SHOW CREATE USER
アカウントを止める ALTER USER ... ACCOUNT LOCK; ERROR 3118
ユーザー名を変える RENAME USER 'a'@'localhost' TO 'b'@'localhost'; mysql.user
ユーザーを消す DROP USER 'app'@'localhost'; mysql.user
ロールを作って配る CREATE ROLE 'r'; GRANT 'r' TO ...; SET DEFAULT ROLE ALL TO ...; SHOW GRANTS ... USING 'r'
今の接続ユーザーを見る SELECT USER(), CURRENT_USER(); 一致しないときはホスト照合を確認

ポイントは「ユーザーは '名前'@'ホスト' の組で1つ」ということです。 'app'@'localhost' と 'app'@'%' は別アカウントで、パスワードも権限も別々に持ちます。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「MySQLユーザー作成と権限付与|GRANT・SHOW GRANTS」

検証環境と対象バージョン

項目 内容
MySQL 8.0.46(Ubuntu パッケージ、--initialize-insecure で初期化)
接続 ソケット(-S)と TCP(-h127.0.0.1 -P7246)の両方
既定の認証プラグイン caching_sha2_password
skip_name_resolve 0(既定)

MySQL 8.0 は2026年4月にサポートが終了しています。 この記事のSQLは、長期サポート版の MySQL 8.4 LTS と 9.7 LTS でも同じ手順で使えます。違いが出るのは次の点です(公式ドキュメントで確認)。

項目 8.0 8.4 9.x(9.7含む)
CREATE USER → GRANT の手順 同じ 同じ 同じ
mysql_native_password 使える(非推奨) 既定で無効(起動オプションで有効化) 削除済み
default_authentication_plugin 変数 あり 削除(authentication_policy を使う) 削除
SUPER 権限 非推奨 非推奨 非推奨(動的権限へ移行)

以下の実行結果は、すべて手元の MySQL 8.0.46 で実行したものです。

手順1:ユーザーを作って権限を付け、確認する

まずは一連の流れをコピペで流せる形で示します。root(または CREATE USER 権限を持つユーザー)で接続して実行します。

-- 練習用のデータベースとテーブル
CREATE DATABASE shop;
CREATE TABLE shop.orders (
  id INT PRIMARY KEY AUTO_INCREMENT,
  member_id INT,
  item VARCHAR(50),
  price INT,
  note VARCHAR(100)
);
INSERT INTO shop.orders (member_id, item, price, note)
VALUES (1, 'notebook', 300, 'a'), (2, 'pen', 120, 'b');

-- 1. ユーザーを作る
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'Str0ng!Pass#2026';
SHOW GRANTS FOR 'app_user'@'localhost';

-- 2. 権限を付ける
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'app_user'@'localhost';
SHOW GRANTS FOR 'app_user'@'localhost';

-- 3. 一部を取り消す
REVOKE DELETE ON shop.* FROM 'app_user'@'localhost';
SHOW GRANTS FOR 'app_user'@'localhost';

実行結果(MySQL 8.0.46):

+----------------------------------------------+
| Grants for app_user@localhost                |
+----------------------------------------------+
| GRANT USAGE ON *.* TO `app_user`@`localhost` |
+----------------------------------------------+
+----------------------------------------------------------------------------+
| Grants for app_user@localhost                                              |
+----------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `app_user`@`localhost`                               |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `shop`.* TO `app_user`@`localhost` |
+----------------------------------------------------------------------------+
+--------------------------------------------------------------------+
| Grants for app_user@localhost                                      |
+--------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `app_user`@`localhost`                       |
| GRANT SELECT, INSERT, UPDATE ON `shop`.* TO `app_user`@`localhost` |
+--------------------------------------------------------------------+

作成直後の GRANT USAGE ON *.* は「ログインはできるが、何の権限もない」という意味です。 権限がゼロでもこの1行は必ず表示されます。また、MySQL 8.0 では GRANT / REVOKE / CREATE USER は権限テーブルへ即時反映されるため、FLUSH PRIVILEGES は不要です(mysql.user などを直接 UPDATE したときだけ必要)。

付与した権限が実際に効いているかは、作ったユーザーでログインして確かめます。

mysql -u app_user -p -S /tmp/mysql.sock -e "SELECT USER(), CURRENT_USER(); DELETE FROM shop.orders;"
+--------------------+--------------------+
| user()             | current_user()     |
+--------------------+--------------------+
| app_user@localhost | app_user@localhost |
+--------------------+--------------------+
ERROR 1142 (42000) at line 1: DELETE command denied to user 'app_user'@'localhost' for table 'orders'

REVOKE した DELETE は ERROR 1142 で拒否されました。 USER() は「ログイン時に名乗ったユーザーと接続元」、CURRENT_USER() は「実際に照合されたアカウント」です。この2つがずれているときは、後述のホスト照合を疑います。

手順2:変更・ロック・名前変更・削除

-- パスワード変更
ALTER USER 'app_user'@'localhost' IDENTIFIED BY 'N3w!Pass#2026';

-- アカウントの一時停止と再開
ALTER USER 'app_user'@'localhost' ACCOUNT LOCK;
ALTER USER 'app_user'@'localhost' ACCOUNT UNLOCK;

-- 名前を変える(権限も引き継がれる)
RENAME USER 'app_user'@'localhost' TO 'shop_app'@'localhost';

-- 全権限を外す(アカウントは残る)
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'shop_app'@'localhost';
SHOW GRANTS FOR 'shop_app'@'localhost';

-- 削除
DROP USER 'shop_app'@'localhost';

REVOKE ALL 後の SHOW GRANTS は GRANT USAGE ON *.* TOshop_app@localhost`` の1行だけになりました。

ロックしたアカウントでログインすると、次のエラーになります。

ERROR 3118 (HY000): Access denied for user 'col_user'@'localhost'. Account is locked.

退職・異動などで一時的に止めたいときは DROP USER より ACCOUNT LOCK が便利です。 権限設定を残したまま、戻すときは ACCOUNT UNLOCK だけで済みます。

よく出るエラーの再現と直し方

ERROR 1410:GRANTでユーザーを作ろうとした

MySQL 5.7 までの記事でよく見る「GRANT でユーザー作成と権限付与を同時に行う」書き方は、8.0 以降では使えません。

GRANT SELECT ON shop.* TO 'nobody'@'localhost';
ERROR 1410 (42000): You are not allowed to create a user with GRANT

IDENTIFIED BY を付けると、構文自体が受け付けられません。

GRANT SELECT ON shop.* TO 'nobody'@'localhost' IDENTIFIED BY 'x';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IDENTIFIED BY 'x'' at line 1

直し方:CREATE USER → GRANT の2文に分けます。

CREATE USER 'nobody'@'localhost' IDENTIFIED BY 'Nobody#2026';
GRANT SELECT ON shop.* TO 'nobody'@'localhost';

ERROR 1045:パスワード違い、またはホストの取り違え

ERROR 1045 (28000): Access denied for user 'app_user'@'localhost' (using password: YES)

1045 はパスワードが違うときだけでなく、「想定と違うホストのアカウントに照合された」ときにも出ます。 次は 'demo'@'%' と 'demo'@'localhost' を別パスワードで作った例です。

CREATE USER 'demo'@'%'         IDENTIFIED BY 'PctPass#1';
CREATE USER 'demo'@'localhost' IDENTIFIED BY 'LocalPass#1';
接続方法 使ったパスワード 結果(実行結果)
ソケット % 側(PctPass#1) ERROR 1045 ... 'demo'@'localhost'
ソケット localhost 側(LocalPass#1) demo@localhost で成功
TCP -h127.0.0.1 % 側(PctPass#1) ERROR 1045 ... 'demo'@'localhost'
'demo'@'localhost' を DROP 後、ソケット % 側 demo@% で成功

MySQL は、ホスト指定がより具体的なアカウントを優先して照合します。 localhost からの接続には 'demo'@'localhost' が先に当たるので、% 側のパスワードでは通りません。また skip_name_resolve=0(既定)の環境では、127.0.0.1 からの TCP 接続も localhost として照合されました。

1045 の切り分けは次の順で確認すると早いです。

確認すること SQL・コマンド
同名ユーザーが複数ホストにないか SELECT user, host FROM mysql.user WHERE user='demo';
どのアカウントに当たったか エラー文の 'ユーザー'@'ホスト' を読む
パスワードを付けて送ったか エラー文の using password: YES/NO
ロック・期限切れでないか SELECT account_locked, password_expired FROM mysql.user ...;

接続まわりのエラー全般(2002・2003など)は、別記事「MySQLコマンド一覧」の接続エラー表とあわせて確認してください。

ERROR 1044・1142・1143:権限が足りない

コード 意味 実行結果のメッセージ
1044 DB単位の権限がない Access denied for user 'app_user'@'localhost' to database 'mysql'
1142 テーブル単位の操作権限がない CREATE command denied to user 'app_user'@'localhost' for table 't'
1143 カラム単位の権限がない UPDATE command denied to user 'col_user'@'localhost' for column 'price' in table 'orders'

直し方は、エラー文に出た操作(CREATE、UPDATEなど)とスコープ(DB・テーブル・カラム)をそのまま GRANT に書くことです。

ERROR 1396:同じユーザーを作った・ないユーザーを消した

ERROR 1396 (HY000): Operation CREATE USER failed for 'app_user'@'localhost'
ERROR 1396 (HY000): Operation DROP USER failed for 'ghost'@'localhost'

スクリプトで何度も流す場合は IF NOT EXISTS / IF EXISTS を付けます。既存ユーザーに CREATE USER IF NOT EXISTS を流すと、エラーではなく Note になります。

+-------+------+---------------------------------------------------------+
| Level | Code | Message                                                 |
+-------+------+---------------------------------------------------------+
| Note  | 3163 | Authorization ID 'app_user'@'localhost' already exists. |
+-------+------+---------------------------------------------------------+

IF NOT EXISTS ではパスワードは上書きされません。 パスワードを確実にそろえたいときは、続けて ALTER USER ... IDENTIFIED BY を実行します。

権限の種類とスコープの一覧

GRANT の ON の書き方で、権限の効く範囲(スコープ)が決まります。

スコープ ON の書き方 保存先テーブル 主な用途
グローバル ON *.* mysql.user 管理者、バックアップ、レプリケーション
データベース ON shop.* mysql.db アプリ用・読み取り専用
テーブル ON shop.orders mysql.tables_priv 特定テーブルだけ公開
カラム SELECT (id, item) ON shop.orders mysql.columns_priv 一部の列だけ見せる
ストアドルーチン ON PROCEDURE shop.p1 mysql.procs_priv 実行だけ許可

主な権限と、付けられるスコープは次のとおりです(MySQL 8.4 リファレンス「Privileges Provided by MySQL」で確認)。

権限 できること グローバル DB テーブル カラム
SELECT 参照 ○ ○ ○ ○
INSERT 追加 ○ ○ ○ ○
UPDATE 更新 ○ ○ ○ ○
DELETE 削除 ○ ○ ○ -
CREATE / DROP / ALTER テーブル作成・削除・変更 ○ ○ ○ -
INDEX インデックス作成・削除 ○ ○ ○ -
REFERENCES 外部キー作成 ○ ○ ○ ○
CREATE VIEW / SHOW VIEW ビュー作成・定義参照 ○ ○ ○ -
TRIGGER トリガー操作 ○ ○ ○ -
EXECUTE / CREATE ROUTINE / ALTER ROUTINE ストアド実行・作成・変更 ○ ○ - -
EVENT イベント操作 ○ ○ - -
LOCK TABLES 明示的なテーブルロック ○ ○ - -
CREATE TEMPORARY TABLES 一時テーブル作成 ○ ○ - -
PROCESS 全スレッドの参照 ○ - - -
RELOAD FLUSH 系の実行 ○ - - -
REPLICATION SLAVE レプリカからの接続 ○ - - -
REPLICATION CLIENT レプリケーション状態の参照 ○ - - -
CREATE USER ユーザー作成・変更・削除 ○ - - -
CREATE ROLE / DROP ROLE ロール作成・削除 ○ - - -
FILE サーバー上のファイル読み書き ○ - - -
GRANT OPTION 自分の権限を他人に渡す ○ ○ ○ ○
ALL PRIVILEGES そのスコープの全権限(GRANT OPTION除く) ○ ○ ○ -

8.0 からは SUPER を細かく分けた「動的権限」が増えています。 代表的なものは SYSTEM_VARIABLES_ADMIN(グローバル変数変更)、CONNECTION_ADMIN(接続制御・KILL)、BINLOG_ADMIN、REPLICATION_SLAVE_ADMIN、BACKUP_ADMIN、ROLE_ADMIN です。新しく管理者を作るときは SUPER ではなく必要な動的権限だけを付けると、8.4・9.x への移行もスムーズです。

カラム単位の権限:実行結果

CREATE USER 'col_user'@'localhost' IDENTIFIED BY 'ColUser#2026';
GRANT SELECT (id, item, price), UPDATE (note) ON shop.orders TO 'col_user'@'localhost';

col_user でログインして実行した結果:

mysql> SELECT id, item, price FROM shop.orders;
+----+----------+-------+
| id | item     | price |
+----+----------+-------+
|  1 | notebook |   300 |
|  2 | pen      |   120 |
+----+----------+-------+

mysql> SELECT * FROM shop.orders;
ERROR 1142 (42000): SELECT command denied to user 'col_user'@'localhost' for table 'orders'

mysql> UPDATE shop.orders SET price = 1 WHERE id = 1;
ERROR 1143 (42000): UPDATE command denied to user 'col_user'@'localhost' for column 'price' in table 'orders'

カラム権限を付けたテーブルでは SELECT * が使えなくなります。 列名を明示して取得します。列を絞る用途ではビューを作って SELECT を付ける方法も扱いやすく、どちらを使うかは「列の組み合わせが固定ならビュー、ユーザーごとに違うならカラム権限」が目安です。

用途別の権限テンプレート

用途 ホスト 付ける権限 スコープ
読み取り専用(集計・BI) % または接続元のサブネット SELECT, SHOW VIEW shop.*
アプリケーション アプリサーバーのIP SELECT, INSERT, UPDATE, DELETE shop.*
マイグレーション 作業端末 上記+CREATE, ALTER, DROP, INDEX, REFERENCES shop.*
バックアップ(mysqldump) localhost SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, PROCESS, RELOAD *.*
レプリケーション レプリカのIP REPLICATION SLAVE *.*
-- 読み取り専用:有効期限90日、3回失敗で1日ロック
CREATE USER 'report_ro'@'%' IDENTIFIED BY 'ReportRo#2026'
  PASSWORD EXPIRE INTERVAL 90 DAY
  FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 1;
GRANT SELECT, SHOW VIEW ON shop.* TO 'report_ro'@'%';

-- アプリケーション用(接続元を限定)
CREATE USER 'shop_app'@'10.0.1.%' IDENTIFIED BY 'ShopApp#2026';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'10.0.1.%';

-- バックアップ用(mysqldump)
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'Backup#2026';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, PROCESS, RELOAD
  ON *.* TO 'backup'@'localhost';

-- レプリケーション用(暗号化された接続を必須に)
CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl#2026' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

mysqldump に必要な権限を確かめた結果

SELECT だけを付けたユーザーで mysqldump を実行すると、次の2つのエラーが出ました。

mysqldump: Error: 'Access denied; you need (at least one of) the PROCESS privilege(s) for this operation' when trying to dump tablespaces
mysqldump: Got error: 1044: Access denied for user 'bk2'@'localhost' to database 'shop' when using LOCK TABLES

同じユーザーでも --single-transaction --no-tablespaces を付けると終了コード 0 で完了しました。公式ドキュメントの説明どおり、--single-transaction を使えば LOCK TABLES は不要、--no-tablespaces を使えば PROCESS は不要です。上のテンプレート(backup ユーザー)では --single-transaction --routines --events で正常に取得できました。オプションの詳細は別記事「mysqldumpバックアップとリストア」を参照してください。

パスワードの失敗回数ロック:実行結果

report_ro に3回続けて違うパスワードを送った結果です。

ERROR 1045 (28000): Access denied for user 'report_ro'@'localhost' (using password: YES)
ERROR 1045 (28000): Access denied for user 'report_ro'@'localhost' (using password: YES)
ERROR 3955 (HY000): Access denied for user 'report_ro'@'localhost'. Account is blocked for 1 day(s) (1 day(s) remaining) due to 3 consecutive failed logins.

ロック中は正しいパスワードでも 3955 になります。 管理者が ALTER USER 'report_ro'@'%' ACCOUNT UNLOCK; を実行すると、すぐにログインできるようになりました。

localhost と % の違い

ホスト指定 照合される接続 向いている用途
'localhost' 同じサーバー上からのソケット接続(名前解決が有効なら 127.0.0.1 も) 管理者、バックアップ
'127.0.0.1' IPv4 ループバックへの TCP 接続 コンテナ内からの TCP 接続
'10.0.1.%' 10.0.1.0/24 からの接続 アプリサーバー群
'%' どこからでも 開発環境、接続元が変わる用途

% は便利ですが、同じユーザー名の localhost アカウントがあると、そちらが優先されます。 先ほどの検証のとおり、'demo'@'localhost' を消すと、ソケット接続でも demo@% に照合されました。本番のアプリ用は接続元のIP範囲を書くと、意図しない場所からの接続を防げます。

caching_sha2_password と接続エラー2061

MySQL 8.0 以降の既定の認証方式は caching_sha2_password です。暗号化されていない TCP 接続で、まだキャッシュされていないユーザーがログインすると、ERROR 2061 になります。

mysql -u demo -p -h 127.0.0.1 -P 7246 --ssl-mode=DISABLED
ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.
対処 内容
TLSで接続する(推奨) --ssl-mode を既定(PREFERRED)のままにする。今回の環境では TLS_AES_256_GCM_SHA384 で接続
公開鍵を取得する --get-server-public-key を付ける。今回の環境ではこれで成功
ソケット接続にする 同一サーバー内なら -S で接続
ドライバを更新する 古いクライアントライブラリは caching_sha2_password に未対応のことがある

一度ログインに成功するとサーバー側にキャッシュされ、同じ条件でも次回からは通りました(ALTER USER でパスワードを変えるとキャッシュは消えます)。8.4 では mysql_native_password が既定で無効、9.x では削除されているため、古い認証方式に戻すより、クライアント側を caching_sha2_password 対応にするのが長く使える方法です。

ロールでまとめて権限を管理する

同じ権限を何人にも配るときは、ロール(権限の束)を使います。

CREATE ROLE 'shop_read', 'shop_write';
GRANT SELECT ON shop.* TO 'shop_read';
GRANT INSERT, UPDATE, DELETE ON shop.* TO 'shop_write';

CREATE USER 'hanako'@'localhost' IDENTIFIED BY 'Hanako#2026';
GRANT 'shop_read', 'shop_write' TO 'hanako'@'localhost';

ここで hanako としてログインすると、ロールを付けたのに SELECT が拒否されます。

+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+
ERROR 1142 (42000): SELECT command denied to user 'hanako'@'localhost' for table 'orders'

ロールは「付与」と「有効化」が別だからです。既定ロールを設定すると、ログイン時に自動で有効になります。

SET DEFAULT ROLE ALL TO 'hanako'@'localhost';
+----------------------------------+
| CURRENT_ROLE()                   |
+----------------------------------+
| `shop_read`@`%`,`shop_write`@`%` |
+----------------------------------+
+----------+
| count(*) |
+----------+
|        2 |
+----------+
方法 効く範囲 使いどころ
SET DEFAULT ROLE ALL TO ユーザー そのユーザーの次回以降のログイン 通常はこれ
SET ROLE ALL;(ログイン後に実行) 今のセッションだけ 一時的に切り替える
activate_all_roles_on_login=ON サーバー全体 全員に自動で有効化したい
mandatory_roles 全ユーザーに強制付与 共通の参照権限など

ロールが持つ権限まで展開して確認するには USING を使います。

SHOW GRANTS FOR 'hanako'@'localhost' USING 'shop_read', 'shop_write';
| GRANT USAGE ON *.* TO `hanako`@`localhost`                               |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `shop`.* TO `hanako`@`localhost` |
| GRANT `shop_read`@`%`,`shop_write`@`%` TO `hanako`@`localhost`           |

存在しないロールを付けようとすると ERROR 3523 (HY000): Unknown authorization ID になります。なお、ロールは mysql.user に「ロック済み・パスワード期限切れ」のアカウントとして保存されるため、ロール名でログインすることはできません。PostgreSQL のロール設計と比べたい場合は、別記事「PostgreSQLのロールと権限」も参考になります。

ユーザー一覧と権限を確認するSQL

-- ユーザー一覧(システムアカウントを除く)
SELECT user, host, plugin,
       account_locked AS locked, password_expired AS expired,
       password_last_changed
FROM mysql.user
WHERE user NOT LIKE 'mysql.%'
ORDER BY user, host;

実行結果(抜粋):

+------------+-----------+-----------------------+--------+---------+-----------------------+
| user       | host      | plugin                | locked | expired | password_last_changed |
+------------+-----------+-----------------------+--------+---------+-----------------------+
| hanako     | localhost | caching_sha2_password | N      | N       | 2026-10-02 00:56:42   |
| shop_read  | %         | caching_sha2_password | Y      | Y       | 2026-10-02 00:56:42   |
| shop_write | %         | caching_sha2_password | Y      | Y       | 2026-10-02 00:56:42   |
+------------+-----------+-----------------------+--------+---------+-----------------------+
確認したいこと SQL
あるユーザーの権限 SHOW GRANTS FOR 'ユーザー'@'ホスト';
自分の権限 SHOW GRANTS;
作成時の設定(期限・ロック等) SHOW CREATE USER 'ユーザー'@'ホスト';
DB単位の権限を一覧 SELECT * FROM information_schema.SCHEMA_PRIVILEGES;
テーブル単位の権限を一覧 SELECT * FROM information_schema.TABLE_PRIVILEGES;
カラム単位の権限を一覧 SELECT * FROM information_schema.COLUMN_PRIVILEGES;
ロールの付与関係 SELECT * FROM mysql.role_edges;
既定ロール SELECT * FROM mysql.default_roles;
使える権限の一覧 SHOW PRIVILEGES;

information_schema の各 PRIVILEGES ビューは権限を1行ずつに分けて返すので、スクリプトでの監査に向いています。

パスワードの有効期限と変更の強制

-- 次回ログイン時に変更させる
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE;
-- 90日ごとに期限切れ
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
-- 期限なし
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE NEVER;

期限切れのユーザーが mysql -e で接続すると、次のように表示されて止まります。

Please use --connect-expired-password option or invoke mysql in interactive mode.

--connect-expired-password で接続すると、パスワード変更以外の文は ERROR 1820 になります。

ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
-- 本人がパスワードを変える
ALTER USER USER() IDENTIFIED BY 'N3w!Pass#2026';

アプリ用アカウントに有効期限を付けると、期限が来た時点でアプリの接続が止まります。 アプリ用は PASSWORD EXPIRE NEVER にして別の手順でローテーションし、人が使うアカウントに期限を付ける、という分け方が扱いやすいです。

よくある質問

Q. GRANTの後に FLUSH PRIVILEGES は必要ですか? A. GRANT / REVOKE / CREATE USER / ALTER USER を使った場合は不要です。権限テーブルを UPDATE などで直接書き換えたときだけ必要になります。

Q. ユーザー作成時に IDENTIFIED BY を書かないとどうなりますか? A. パスワードなしのアカウントになり、ユーザー名だけでログインできる状態になります。作成時は必ず IDENTIFIED BY でパスワードを指定し、既存のアカウントは SELECT user, host FROM mysql.user WHERE authentication_string = ''; で確認できます。

Q. 'user'@'%' で作ったのに、ローカルからログインできません。 A. 同じユーザー名で localhost のアカウント(匿名ユーザー ''@'localhost' を含む)があると、そちらが優先されます。SELECT user, host FROM mysql.user; で同名・匿名のアカウントがないか確認してください。

Q. MySQL 8.4 や 9.7 でもこの手順で使えますか? A. 使えます。CREATE USER・GRANT・SHOW GRANTS・ロールの書き方は同じです。mysql_native_password を指定しているアカウントは、8.4 では既定で無効、9.x では使えないため、caching_sha2_password へ移行してから更新してください。

まとめ

  • MySQL 8.0以降は CREATE USER → GRANT の2段階。GRANTだけでは ERROR 1410
  • アカウントは '名前'@'ホスト' の組。localhost と % は別アカウントで、具体的な方が優先される
  • 権限は *.*・db.*・db.table・カラムの4スコープ。必要な範囲だけ付ける
  • 確認は SHOW GRANTS、ロール込みなら USING、監査は information_schema の各PRIVILEGESビュー
  • ロールは付与後に SET DEFAULT ROLE で有効化。付けただけでは ERROR 1142
  • mysqldump用は --single-transaction --no-tablespaces なら権限を絞れる
  • 8.4・9.7でも同じ手順で使える。認証は caching_sha2_password 対応のクライアントで

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「MySQLユーザー作成と権限付与|GRANT・SHOW GRANTS」

株式会社RJCでは、MySQLのユーザー設計と権限管理を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?