PostgreSQLの権限まわりでつまずく場面は、だいたい決まっています。「ユーザーを作ったのにログインできない」「SELECT権限を付けたのにテーブルが見えない」「開発中は動いていたのに、マイグレーションの実行者を変えたら権限が付かなくなった」。どれも構文の誤りではなく、権限がどの単位で判定され、いつ決まるのかを取り違えたことが原因です。
この記事では、ロールという単一の実体で人もグループもアプリも表す設計、CREATE ROLEで指定できる属性とその既定値、所有者を起点に効くGRANTの判定順、そして将来のオブジェクトへ権限を配るALTER DEFAULT PRIVILEGESまでを扱います。データベース全般の位置づけはデータベースとは?種類・DBMS・RDBとNoSQLの選び方に、スキーマ側の名前解決とpublicスキーマの既定はPostgreSQLのスキーマ運用|search_pathの解決順とpublic権限に譲ります。動作の前提は18系(2026年8月時点、最新のマイナーは18.6)としました。
まとめ:ロールと権限で先に決める4点
先に結論を4点で示します。第一に、PostgreSQLに「ユーザー」という独立した実体はありません。あるのはロールだけで、LOGIN属性が付いているかどうかが人の接続する口かどうかを分けます。CREATE USERとCREATE ROLEの違いも、この属性が既定で付くかどうかの一点だけです。
第二に、CREATE ROLEの属性はすべて否定側が既定になっています。スーパーユーザーでもDB作成でもロール作成でもレプリケーションでもRLS迂回でもない、という状態から始まり、必要な属性だけを足していく形です。ここを逆に理解して「とりあえずスーパーユーザーで作る」運用に流れると、後から権限を絞る作業が丸ごと残ります。
第三に、権限は所有者を起点に配られます。オブジェクトを作った瞬間、それを操作できるのは所有者とスーパーユーザーだけで、他のロールには何も付いていません。そのうえで、テーブルへ到達するにはスキーマのUSAGEとテーブルの操作権限が両方そろう必要があり、片方だけでは弾かれます。この二段判定は、AIエージェントへ参照だけを開けるPostgreSQL MCPサーバーの権限設計でもそのまま効きます。
第四に、ALTER DEFAULT PRIVILEGESは既存のオブジェクトへ遡りません。効くのは以後に作られるものだけで、しかも「誰が作ったか」で適用される既定が変わります。マイグレーションの実行ロールを途中で変えると権限が付かなくなるのは、この仕様が理由です。以下、順に根拠を見ていきます。
ロールとユーザーが同じ実体でLOGIN属性だけで分かれる仕組み
PostgreSQLの権限主体はロールに一本化されています。8.1より前にあったユーザーとグループの区別は統合され、いまはどちらもロールです。人が使うアカウントも、アプリケーションが接続する専用アカウントも、権限をまとめるためだけの入れ物も、すべて同じpg_authidに並びます。
CREATE USERとCREATE ROLEが生む差がログイン可否だけである理由
公式ドキュメントはCREATE USERをCREATE ROLEの別名と位置づけており、差はLOGIN属性の既定値だけです。CREATE ROLEの既定はNOLOGINで、CREATE USERにはLOGINが付きます。
-- アプリ接続用(ログインする)
CREATE ROLE app_rw LOGIN PASSWORD 'xxxx';
-- 権限をまとめる入れ物(ログインしない)
CREATE ROLE role_reporting;
実務では、ログインする側と権限を束ねる側を名前で分けておくと後の棚卸しが楽になります。接続してくるのは前者だけ、権限を持つのは後者、という二層にしておけば、担当者の入れ替わりでロールを作り直しても、付与のやり直しが発生しません。
ロールがクラスタ全体で共有されデータベース単位で分かれない範囲
ロールはデータベースごとではなくクラスタ全体で共有される仕組みです。同じクラスタに複数のデータベースを同居させている場合、片方のデータベースのために作ったロールは、もう片方からも見えます。分離したいのは接続そのものなので、CONNECT権限をデータベース単位で外す設計が必要になります。
誰がどんな属性を持っているかは、psqlの\duで一覧できる仕様です。カタログ側から機械的に取りたい場合はpg_rolesビューを読みます。一覧系のメタコマンドとカタログの使い分けはPostgreSQLのテーブル一覧を取得する方法|メタコマンドとカタログの使い分けで整理しています。
CREATE ROLEで指定する属性の既定値と付けてよい範囲の線引き
属性は、そのロールが「何をできる立場か」を決めます。個々のテーブルに対する権限とは別の層にあり、GRANTでは配れません。既定値は次のとおりで、すべて否定側から始まります。
| 属性 | 既定 | 与える力 |
|---|---|---|
| SUPERUSER | NOSUPERUSER | すべての権限検査を素通り |
| CREATEDB | NOCREATEDB | データベースの作成 |
| CREATEROLE | NOCREATEROLE | ロールの作成と変更 |
| LOGIN | NOLOGIN | クライアントからの接続 |
| REPLICATION | NOREPLICATION | レプリケーション接続 |
| BYPASSRLS | NOBYPASSRLS | 行レベルの制御を迂回 |
| INHERIT | INHERIT | 所属先の権限を自動で継承 |
| CONNECTION LIMIT | -1 | 同時接続数の上限(無制限) |
SUPERUSERとREPLICATIONとBYPASSRLSを分けて渡す判断の基準
この3つは性質が違います。SUPERUSERは権限検査そのものを飛ばすため、監査で「誰が何をできるか」を説明できなくなります。付けてよいのは初期構築と障害対応に限り、日常の接続には使わない、という線引きが現実的です。REPLICATIONはレプリケーション接続とバックアップ取得のために必要ですが、クラスタ全体のデータを読み出せる強い属性なので、その用途の専用ロールに閉じます。BYPASSRLSは行レベルの制御を無効化するので、後述の行単位アクセス制御を入れる予定があるなら、バッチ用ロールへ安易に付けないでください。
強い属性を配らずに運用したい場面では、定義済みロールが使えます。監視系ならpg_monitor、全テーブルの読み取りならpg_read_all_data、バキュームや再索引などの保守作業ならpg_maintain(17で追加)があり、それぞれ必要な範囲だけを渡せます。pg_read_all_dataとpg_write_all_dataは行レベルの制御を迂回しない点も、設計上は扱いやすい性質です。
CREATEROLE権限が16以降でADMIN OPTIONの範囲に限定された変更
ここは版によって挙動が変わった箇所です。15以前のCREATEROLEは、スーパーユーザー以外のほぼすべてのロールを変更できました。パスワードの変更も可能だったため、実質的な昇格経路になっていました。16以降は、ADMIN OPTIONを持っている相手のロールしか変更・削除できません。
そして、スーパーユーザーでないロールがCREATEROLEで新しいロールを作ると、作成されたロールが作成者へ自動的に付与されます。付与の内容はADMIN TRUE, SET FALSE, INHERIT FALSEに相当し、管理はできるが権限は継承しない、という最小限の形です。この自動付与の挙動はcreaterole_self_grantで調整できます。
運用への影響は明確です。15以前の環境で「CREATEROLEを付けたロールに利用者管理を任せる」という設計を組んでいた場合、16以降へ上げると管理対象が自分の作ったロールだけに狭まります。稼働中の版の確認手順とサポート期限の判定はPostgreSQLのバージョン確認方法|サーバ・クライアント別コマンドとEOL判定にまとめてあります。なお14系は2026年11月12日が最終リリース予定です。
パスワードの保存方式とmd5が非推奨になった18の警告への対応
パスワードはカタログに暗号化されて保存され、方式はpassword_encryptionで決まります。18では、md5でパスワードを設定するとCREATE ROLEとALTER ROLEが非推奨の警告を出します。将来のメジャー版で削除される予定のため、警告が出た環境はscram-sha-256への切り替えを計画に入れてください。警告そのものはmd5_password_warningsで止められますが、それは対処ではなく先送りです。認証方式の切り替え手順と、通信・保存時を含む暗号化の全体像はPostgreSQLの暗号化|TLS設定・pgcryptoの列暗号化とTDE不在時の選び方で整理しています。
切り替えは、設定を変えたうえで各ロールのパスワードを設定し直すと反映されます。接続側の認証方式はpg_hba.confで決まるため、サーバの設定だけを変えても既存の行が古い方式のままなら効きません。初期の認証設定と接続確認までの手順はPostgreSQLのインストール手順|Windows・Ubuntu別の導入とinitdbを参照してください。18では外部のIDプロバイダを使うoauth認証方式も追加されています。
GRANTとREVOKEが所有者を起点に効く二段構造と確認の手順
オブジェクトを作ったロールが所有者になり、初期状態では所有者とスーパーユーザーだけが操作できます。他のロールへ渡すにはGRANTが要ります。テーブルに対して指定できるのはSELECT、INSERT、UPDATE、DELETE、TRUNCATE、REFERENCES、TRIGGER、そして17で追加されたMAINTAINです。
スキーマのUSAGEとテーブルの操作権限が二段で判定される順序
よくある行き違いが、テーブルへSELECTを付けたのに読めない、という症状です。権限は「スキーマへの到達」と「オブジェクトへの操作」の二段で判定されるためです。スキーマのUSAGEが無ければ、その下のテーブルにどんな権限が付いていても到達できません。
GRANT USAGE ON SCHEMA sales TO role_reporting;
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO role_reporting;
GRANT role_reporting TO app_ro;
スキーマ側の既定がどうなっているかは版によって差があり、publicスキーマの扱いは15で変わりました。その詳細は前掲のスキーマ運用の記事で扱っています。
権限一覧のaclitem表記を読み解いて実際の付与状態を確かめる手順
付与の結果はpsqlの\dp(別名\z)で確認します。表示は被付与者=権限略号/付与者という形式で、被付与者が空欄の行はPUBLIC、つまり全ロールへの付与を意味します。略号のうしろにアスタリスクが付いていれば、その権限を他へ渡す権限(グラントオプション)も持っている状態です。スキーマなら\dn+、データベースなら\lで同じように読めます。
特定の判定だけを機械的に確かめたい場合は、has_table_privilegeのような関数を使うと、継承経路まで含めた実効的な可否が返ります。棚卸しの自動化では、一覧表示から目視で追うよりこちらのほうが確実です。
PUBLICへ既定で付く権限を外しておくべき対象と外し方の判断基準
PostgreSQLは、一部のオブジェクトに対してPUBLICへ既定で権限を付けます。テーブル、列、シーケンス、スキーマ、テーブル空間には何も付きません。一方でデータベースにはCONNECTとTEMPORARYが、関数と手続きにはEXECUTEが、手続き言語とデータ型にはUSAGEが既定で付きます。
業務システムで先に見直したいのは、データベースのCONNECTと関数のEXECUTEです。前者はクラスタ内の別システム用ロールからの接続を許してしまい、後者は自作関数を誰でも実行できる状態を作ります。いずれもPUBLICからREVOKEしたうえで、必要なロールへ個別に付け直す形にします。
ロールのメンバーシップでINHERITとSETを分けて設計する方法
権限をまとめる入れ物としてのロールは、GRANT 入れ物 TO 利用者で所属させます。16以降は、この所属にINHERIT、SET、ADMINの3つのオプションを個別に指定できます。
継承が途中で止まる条件とSET ROLEで切り替える使い分け
INHERITが真の所属は、入れ物の権限を自動的に使える状態です。ただし継承の連鎖は、途中に偽の所属が挟まるとそこで止まります。管理者ロールの権限は自動で使わせたくないが、必要なときだけ切り替えて使わせたい、という設計であればSETだけを真にします。
GRANT role_admin TO alice WITH INHERIT FALSE, SET TRUE;
-- 通常は継承しない。必要なときだけ明示的に切り替える
SET ROLE role_admin;
RESET ROLE;
この分離が効くのは、危険な操作の実行を意図的な一手順にしたい場面です。日常の接続では読み取りだけができ、データ修正のときにだけ切り替える、という運用にすると、事故の範囲を狭められます。
属性が継承されずSET ROLEでしか使えない権限の扱い方と切替条件
注意したいのは、LOGIN、SUPERUSER、CREATEDB、CREATEROLEといった属性が継承されないことです。これらは通常の権限とは別扱いで、所属しているだけでは使えません。実際にその属性を持つロールへSET ROLEしない限り効きません。「管理者ロールに入れたのにデータベースが作れない」という報告の大半は、この仕様です。
ALTER DEFAULT PRIVILEGESで将来のオブジェクトへ配る既定権限
テーブルを追加するたびにGRANTを打ち直す運用は続きません。ALTER DEFAULT PRIVILEGESは、以後に作られるオブジェクトへ自動で権限を付ける仕組みです。対象はスキーマ、テーブル(ビューと外部テーブルを含む)、シーケンス、関数、型、そして18で加わった大きなオブジェクトです。
既存オブジェクトへ遡及しない性質と初期投入時に踏む順番と確認方法
公式ドキュメントは「すでに存在するオブジェクトに割り当てられた権限には影響しない」と明記しています。したがって、導入時は既存分へのGRANTと、将来分への既定設定を両方打つのが正しい順番です。
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO role_reporting;
ALTER DEFAULT PRIVILEGES IN SCHEMA sales
GRANT SELECT ON TABLES TO role_reporting;
なおIN SCHEMAはスキーマ自体と大きなオブジェクトには指定できません。スキーマは入れ子にならず、大きなオブジェクトはスキーマに属さないためです。スキーマ単位の既定は全体の既定に足し算されるので、全体で配ったものをスキーマ単位で取り消すことはできません。
実行者ごとに既定が決まる仕様がマイグレーションで崩れる場面と防止策
もう一つの落とし穴が、既定権限は「オブジェクトを作る現在のロール」のものだけが適用され、所属先のロールから継承されないという仕様です。FOR ROLEを省略すると、コマンドを打った当人の既定として登録されます。
この性質は、CIから流すマイグレーションで表面化します。開発者が手元で設定した既定は、デプロイ用ロールが作ったテーブルには効きません。対策は、テーブルを作るロールを1つに固定し、そのロールをFOR ROLEで明示して既定を登録することです。複数の実行経路があるなら、経路の数だけ登録します。
行レベルセキュリティを権限設計へ組み込むかどうかを決める判断基準
ここまでは、テーブル単位までの制御です。同じテーブルの中で見える行を利用者ごとに変えたい場合は、行レベルセキュリティ(RLS)を使います。仕組みとDB別の設定手順はRow Level Security(RLS)とは?行単位アクセス制御の仕組みとDB別の設定方法で扱っているため、ここでは権限設計に組み込むかどうかの判断に絞ります。
所有者が既定で素通りする挙動とFORCEを付ける必要がある構成
設計時に外せない前提が3つあります。1つ目は、有効化した時点で該当テーブルは既定で拒否になり、方針を1つも書いていなければ何も見えません。2つ目は、スーパーユーザーとBYPASSRLSを持つロールは常に素通りし、テーブルの所有者も通常は素通りします。所有者にも効かせたいならFORCE ROW LEVEL SECURITYを明示します。
3つ目は、参照整合性の検査が行レベルの制御を迂回することです。一意制約や外部キーは整合性を守るために素通りするので、方針の書き方によっては、見えないはずの行の存在が一意制約違反という形で漏れます。テナント分離の要件が厳しいなら、ここは実装前に確かめる箇所です。
アプリ側の絞り込みで足りる場面とRLSへ寄せるべき場面の線引き
RLSを採用する条件は2つです。データベースへ入る経路がアプリ以外にもあること、そしてアプリの不具合で条件が抜けても行を見せてはいけない要件があること。分析ツールやBIから直接つなぐ、あるいは保守要員がpsqlで入る運用があるなら、絞り込みをアプリ側だけに置く設計は成り立ちません。
逆に、経路がアプリ1本に閉じていて、テナントの識別子が必ず条件に入る作りを保証できるなら、方針の維持コストを払ってまで寄せる必要は薄くなります。方針はテーブルごとに書くため、テーブルが増えるほど記述と検証の量が増えるからです。判断は要件の側から決め、途中で足す前提なら、迂回してしまう属性を早い段階で棚卸ししておきます。
PostgreSQLの権限設計を内製で回す条件と外部へ渡すときの線引き
ここまでの内容を自社で回せるかどうかは、3点で判断できます。第一に、ロールの棚卸しを定期的に実施できる体制があるか。付与は増えるだけで、誰も外しません。四半期ごとに\dpとpg_rolesの出力を突き合わせ、使われていない付与を落とす作業を計画に組み込めるかが分かれ目です。
第二に、版差を追えるか。16のCREATEROLE変更、17のMAINTAIN追加、18のmd5警告のように、権限まわりは版ごとに前提が動きます。稼働中の版とサポート期限を把握し、上げる前に影響範囲を洗う手順が要ります。第三に、監査や委託先対応で「誰が何をできるか」を文書として出せるか。出力を人が読める形へ変換する仕組みが無いと、都度の手作業になります。
この3点のいずれかが埋まらない場合は、設計と初期構築を外部に任せ、日常の付与だけを内製する分担が現実的です。当社では、権限設計を含むデータベース運用の引き取りと、社内で回せる形への移行支援を保守運用・内製化支援として提供しています。既存環境の付与状態を棚卸しするところから相談を受けています。
よくある質問
ユーザーとロールはどちらを使えばよいですか?
内部的には同じ実体で、どちらを使っても動作は同じです。実務では、ログインする主体をCREATE USER、権限を束ねる入れ物をCREATE ROLEで作ると、意図が読み取れて棚卸しが楽になります。命名規約でも接頭辞を分けておくと、一覧を見ただけで役割が判別できます。
アプリ用のロールに与える権限は最小でどこまでですか?
データベースのCONNECT、対象スキーマのUSAGE、テーブルの操作権限、シーケンスを使うならUSAGEの4種が基本形です。所有者にはしないでください。所有者にするとDROPやALTERまで通ってしまい、アプリの不具合が構造の破壊につながります。マイグレーション用と実行用は別のロールに分けます。
権限を変更したのに反映されないのはなぜですか?
まず、スキーマのUSAGEが抜けていないかを確認します。次に、既存の接続がセッション内でSET ROLEしている場合、切り替え先のロールで判定されている可能性があります。ALTER DEFAULT PRIVILEGESを打った場合は、それが遡らない仕様であることも確認してください。既存分には別途GRANTが要ります。
スーパーユーザーを使わずに運用できますか?
日常運用の範囲なら可能です。監視はpg_monitor、保守作業はpg_maintain(17以降)、全件読み取りはpg_read_all_dataで置き換えられます。マネージドサービスではそもそもスーパーユーザーが提供されず、同種の定義済みロールで代替する構成が標準です。残るのは拡張の導入など一部の操作で、そこだけ手順を分けます。
ロールのパスワード有効期限は強制できますか?
VALID UNTILで日時を指定すると、その時刻以降はパスワードによる認証が通らなくなります。ただしこれは有効期限であって、定期変更を促す機能ではありません。期限切れの検知と更新の運用を別に用意しないと、期限当日に接続が止まります。外部のIDプロバイダ側で管理する方式も選択肢です。