2026年10月13日
2026年10月13日
PostgreSQLのユーザーと権限の設定方法:アプリ用・読み取り専用ロールの作り方
PostgreSQL では、ユーザーもグループも「ロール」という1つの仕組みで管理します。この記事では、アプリケーション用の読み書きロールと、分析やバックアップ確認用の読み取り専用ロールを、必要最小限の権限で作る手順を説明します。後から作ったテーブルにも権限が付くようにする設定や、接続元の制限、権限の確認方法、ロールを削除するときの注意点まで扱います。
なお、WordPress のコアが正式に対応しているデータベースは MySQL と MariaDB で、PostgreSQL には対応していません(2026年10月現在)。この記事の内容は、WordPress と同じサーバーで動かす別のアプリケーションや、分析用のデータベースなどを想定しています。
ロールの基本
PostgreSQL のロールには、ログインできるもの(一般にユーザーと呼ぶもの)と、ログインできず権限をまとめるだけのもの(グループに相当)があります。CREATE USER は、LOGIN 属性付きの CREATE ROLE と同じ意味です。
権限の考え方で押さえておきたいのは次の3点です。
- オブジェクトには所有者がいる: テーブルを作ったロールが所有者になり、所有者はそのテーブルに何でもできます。アプリケーションの接続用ロールを所有者にすると、アプリの不具合や SQL インジェクションで
DROP TABLEまで通ってしまいます。 - 権限は階層ごとに必要: データベースへの接続(CONNECT)、スキーマの利用(USAGE)、テーブルの操作(SELECT など)は別々に付与します。テーブルにだけ権限を付けても、スキーマの USAGE がなければ参照できません。
- PUBLIC という全員向けの枠がある: 全ロールに付与される既定の権限です。PostgreSQL 15 以降では、新しく作ったデータベースの
publicスキーマに誰でもテーブルを作れる状態ではなくなりましたが、それより前のバージョンや、古いバージョンから移行したデータベースでは残っていることがあります。
設計:所有者・読み書き・読み取り専用を分ける
ここでは次の3つのロールを作ります。
- app_owner: テーブルの所有者。ログインはさせず、マイグレーション(テーブル定義の変更)のときだけ使います。
- app_rw: アプリケーションが接続に使うロール。データの読み書きはできますが、テーブル定義の変更はできません。
- app_ro: 読み取り専用のロール。分析や確認作業に使います。
作業は、スーパーユーザー(通常は postgres)で psql に接続して行います。本番環境では、事前に pg_dump などでバックアップを取ってから進めてください。
手順1:ロールとデータベースを作る
CREATE ROLE app_owner NOLOGIN;
CREATE ROLE app_rw LOGIN;
CREATE ROLE app_ro LOGIN;
CREATE DATABASE appdb OWNER app_owner;
パスワードは SQL に直接書かず、psql のメタコマンドで設定するのがおすすめです。CREATE ROLE ... PASSWORD '...' と書くと、コマンド履歴や、設定によってはサーバーログに平文で残るおそれがあるためです。\password は入力したパスワードをクライアント側で暗号化してから送ります。
\password app_rw
\password app_ro
PostgreSQL 14 以降は、パスワードの保存方式の既定値が scram-sha-256 です。古い md5 方式のまま運用している場合は、password_encryption の設定を確認したうえで、パスワードを設定し直すと新しい方式で保存されます。
手順2:既定の権限を絞る
作成したデータベースに接続し、PUBLIC への余分な権限を外します。
\c appdb
REVOKE ALL ON DATABASE appdb FROM PUBLIC;
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
CREATE SCHEMA app AUTHORIZATION app_owner;
1行目で、権限を与えていないロールがこのデータベースに接続できないようにします。2行目は PostgreSQL 14 以前や移行してきたデータベース向けで、15 以降の新規データベースでは実行しても害はありません。アプリ用のテーブルは public ではなく専用の app スキーマに置くと、権限の範囲がはっきりします。
手順3:接続・スキーマ・テーブルの権限を付ける
GRANT CONNECT ON DATABASE appdb TO app_rw, app_ro;
GRANT USAGE ON SCHEMA app TO app_rw, app_ro;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_rw;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_rw;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_ro;
ON ALL TABLES IN SCHEMA は、実行した時点で存在するテーブルにだけ効きます。シーケンス(連番の採番に使うオブジェクト)への権限を忘れると、INSERT のときに連番が取れずにエラーになるので、読み書きロールにはシーケンスの USAGE と SELECT も付けておきます。
手順4:これから作るテーブルにも権限が付くようにする
マイグレーションで新しいテーブルを追加するたびに GRANT し直すのは漏れの元です。ALTER DEFAULT PRIVILEGES を使うと、指定したロールが今後作るオブジェクトに自動で権限が付きます。
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_rw;
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
GRANT USAGE, SELECT ON SEQUENCES TO app_rw;
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
GRANT SELECT ON TABLES TO app_ro;
ここでつまずきやすいのは FOR ROLE の指定です。この設定は「app_owner が作ったオブジェクト」にだけ効きます。マイグレーションを別のロールで実行してテーブルを作ると、所有者が変わって既定の権限が適用されません。マイグレーション担当のロールは app_owner のメンバーにして、作業時に SET ROLE app_owner; してから実行すると、所有者が揃います。
CREATE ROLE migrator LOGIN;
GRANT app_owner TO migrator;
PostgreSQL 16 ではロールのメンバーシップの扱い(INHERIT や SET のオプション、CREATEROLE 属性の権限範囲)が見直されています。バージョンによって挙動が異なるため、利用中のバージョンのドキュメントで GRANT の説明を確認してください。
手順5:接続元を制限する(pg_hba.conf)
権限とは別に、どこから・どのロールで・どの方式で接続できるかを pg_hba.conf で絞ります。アプリケーションサーバーが 10.0.0.0/24 にある場合の例です。
# TYPE DATABASE USER ADDRESS METHOD
hostssl appdb app_rw 10.0.0.0/24 scram-sha-256
hostssl appdb app_ro 10.0.0.0/24 scram-sha-256
hostssl は TLS で暗号化された接続だけを許可します。サーバー側で TLS を設定していない場合はまず host で動作を確認し、TLS を整えてから切り替えてください。変更後は設定を読み直します。
SELECT pg_reload_conf();
読み直しても反映されない場合は、SELECT * FROM pg_hba_file_rules; で記述ミス(error 列)がないかを確認できます。
権限を確認する
psql のメタコマンドで、設定が意図どおりかを確認します。
\du: ロールの一覧と属性\l: データベースと、その接続権限\dn+: スキーマと権限\dp app.*: テーブルごとの権限\ddp:ALTER DEFAULT PRIVILEGESで設定した既定の権限
関数で個別に確かめることもできます。
SELECT has_table_privilege('app_ro', 'app.orders', 'INSERT');
最後に、実際に app_ro でログインして INSERT が拒否されることを確かめておくと安心です。
ロールを削除するときの注意
所有しているオブジェクトや権限が残っているロールは、そのままでは DROP ROLE できません。所有物を別のロールに移してから、残った権限を外して削除します。REASSIGN OWNED と DROP OWNED はデータベースごとに効くため、そのロールが関わっているデータベースそれぞれで実行します。
REASSIGN OWNED BY old_user TO app_owner;
DROP OWNED BY old_user;
DROP ROLE old_user;
DROP OWNED は、そのロールが所有するオブジェクトを削除します。REASSIGN OWNED を先に実行しないとテーブルごと消えるので、順番を守り、実行前にバックアップを取ってください。
まとめ
- PostgreSQL はユーザーもグループも「ロール」で管理する。アプリの接続用ロールをテーブルの所有者にしない。
- 権限はデータベース(CONNECT)、スキーマ(USAGE)、テーブル(SELECT など)の各階層で付ける。シーケンスの権限も忘れない。
- 今後作るテーブルには
ALTER DEFAULT PRIVILEGES FOR ROLEで権限を付け、マイグレーションは所有者ロールで実行する。 - パスワードは
\passwordで設定し、接続元はpg_hba.confで絞る。 - WordPress コアは PostgreSQL に非対応。WordPress 以外の用途を前提に設計する。