はじめに
RDS for PostgreSQLを作成したあと、アプリケーションからマスターユーザーで接続していないでしょうか。
マスターユーザーはRoleやデータベースを作成できる強い管理ユーザーです。通常のデータ読み書きに使うと、アプリケーションの不具合やSQLインジェクションが権限管理まで波及する可能性があります。
この記事では、RDS作成後に最初に決めたいRole設計と、SQLを実行する前のAWS側チェック、TLSを検証した接続方法を整理します。
長くなるため、次の3本に分けています。
- 本記事: Role設計と接続前の確認
- RDS for PostgreSQL権限設定 app_owner・app_user・app_readonlyを作る(今後公開予定)
- RDS for PostgreSQL権限テスト GRANT漏れと運用設定を確認する(今後公開予定)
先に結論
- マスターユーザーは初期設定や管理作業に限定する
- オブジェクト所有者、マイグレーション、アプリ接続、参照専用を分ける
- ログインしないRoleへ権限をまとめ、ログインRoleへ付与する
- RDSへ到達できる通信元をセキュリティグループで限定する
-
sslmode=verify-fullで証明書と接続先ホスト名を検証する - パスワードをSQLファイルやGitへ残さない
AWSも、RDSのマスターユーザーをアプリケーションから直接使用せず、アプリケーションに必要な最小権限のユーザーを作ることを推奨しています。
AWS公式: Master user account privileges
デフォルト設定とこの記事の推奨を先に整理
この記事では、設定を次の3種類に分けて説明します。
-
デフォルト: RDS、PostgreSQL、または
psqlが初期状態で採用する値 - この記事の推奨: セキュリティと運用を考慮し、明示的に採用する値
- 環境ごとに確認: RDSの作成方法、コンソールのテンプレート、DBパラメータグループなどで変わる値
主な設定を先に整理すると、次のとおりです。
| 項目 | デフォルト | この記事の推奨・確認事項 |
|---|---|---|
| RDSマスターユーザー名 | RDSコンソールではpostgres
|
別名を指定でき、作成後は変更できない。実際の設定値を確認する |
| PostgreSQLのポート | 5432 |
ポートを変更している可能性があるため、RDSの接続情報を確認する |
CREATE ROLEのログイン属性 |
NOLOGIN |
権限Roleと所有者RoleはNOLOGIN、接続用RoleだけLOGINを明示する |
psql・libpqのsslmode
|
prefer |
verify-fullを明示し、CA証明書と接続先ホスト名を検証する |
rds.force_ssl |
RDS for PostgreSQL 15以降は1、14以前は0
|
バージョンと実値を確認する。15以降でもクライアント側の証明書検証は別途必要 |
password_encryption |
RDS for PostgreSQL 14以降はscram-sha-256、13はmd5
|
本記事の対象である15以降では通常デフォルトのままでよいが、実値を確認する |
rds.accepted_password_auth_method |
md5+scram |
SCRAMだけに制限する場合は、既存Roleとドライバーの移行後に変更する |
| パブリックアクセス、Multi-AZ、削除保護など | 作成方法や選択した設定で異なる | デフォルトと思い込まず、対象DBインスタンスの実値を確認する |
デフォルトは、すべてのシステムに最適な推奨値という意味ではありません。特にsslmode=preferはデフォルトですが、接続先の本人性を厳密に検証する設定ではないため、本記事ではverify-fullへ変更します。
前提
- 対象読者: RDS for PostgreSQLを作成し、アプリケーション接続前の初期設定を行う人
- 想定環境: RDS for PostgreSQL 15以降、
psqlを利用できる端末 - 認証方式: 本連載ではパスワード認証を扱う
- 扱わない範囲: RDS Proxy、IAMデータベース認証、レプリケーション、監査ログの詳細
- 実行場所: RDSへ接続可能なVPC内の端末、VPN接続端末、または管理用経路
サンプルの名前、タイムアウト、ネットワーク範囲をそのまま本番へ適用しないでください。対象システムの要件と変更手順に合わせ、検証環境で確認してから適用します。
PostgreSQLのRoleとは
PostgreSQLでは、ログインユーザーと権限グループをどちらも Role として扱います。
Roleという共通の仕組みにLOGIN属性が付いているかどうかで、使い方が変わります。PostgreSQLにRoleとは別の「ユーザー」や「グループ」が用意されているわけではありません。
次の図では、接続できないapp_rwへ読み書き権限をまとめ、そのRoleを接続用のapp_userへ付与しています。
-
app_rw: 自分ではログインできない。読み書き権限をまとめるRole -
app_user: データベースへログインできる。付与されたapp_rwの権限を利用するRole
つまり、app_userへテーブル権限を一つずつ直接付与するのではなく、権限をまとめたapp_rwへ所属させる設計です。
| 設定 |
CREATE ROLEのデフォルト |
意味 |
|---|---|---|
LOGIN / NOLOGIN
|
NOLOGIN |
PostgreSQLへログインできるか |
INHERIT / NOINHERIT
|
INHERIT |
所属Roleの権限を通常の操作で利用するか |
CREATEDB / NOCREATEDB
|
NOCREATEDB |
データベースを作成できるか |
CREATEROLE / NOCREATEROLE
|
NOCREATEROLE |
Roleを作成・管理できるか |
SUPERUSER / NOSUPERUSER
|
NOSUPERUSER |
PostgreSQL上の多くの権限制限を回避できるか |
REPLICATION / NOREPLICATION
|
NOREPLICATION |
レプリケーション接続やスロット管理を行えるか |
BYPASSRLS / NOBYPASSRLS
|
NOBYPASSRLS |
行レベルセキュリティポリシーを回避できるか |
CREATE USERは、LOGINを付けたCREATE ROLEとして扱えます。本連載では仕組みを統一して読みやすくするため、すべてCREATE ROLEを使います。
SQLでは、デフォルトと同じ属性もあえて明示します。たとえばapp_owner NOLOGINと書くことで、接続用ではないという設計意図をレビュー時に確認しやすくするためです。
なお、本記事の想定はPostgreSQL 15以降です。PostgreSQL 16以降では、RoleのINHERIT属性に加えて、Roleを付与するときのWITH INHERITオプションも権限継承に関係します。実際に利用するメジャーバージョンの仕様を確認してください。
権限と所有権は別
PostgreSQLでは、SELECTやINSERTなどの 権限 と、テーブルなどの 所有権 は別です。
テーブル所有者は、原則としてそのテーブルを変更・削除できます。アプリ接続ユーザーを所有者にすると、読み書きだけを許可したつもりでも、ALTER TABLEやDROP TABLEまで実行できる設計になりかねません。
そこで、所有者用Roleはログイン不可にして分離します。
推奨するRole構成
この連載では、次の構成を使います。これはRDSがデフォルトで作る構成ではなく、この記事で明示的に作成する推奨構成です。
| Role | ログイン | デフォルトで存在するか | 用途 | 主な権限 |
|---|---|---|---|---|
| RDSマスターユーザー | あり | あり | 初期設定、Role作成、拡張管理 |
CREATEDB、CREATEROLE、rds_superuserなど |
app_owner |
なし | なし | データベース、スキーマ、テーブルの所有者 | DDL対象の所有権 |
app_migrator |
あり | なし | Flyway、Liquibase、Prismaなどのマイグレーション | 必要時にapp_ownerへ切り替える |
app_rw |
なし | なし | 読み書き権限をまとめる |
SELECT、INSERT、UPDATE、DELETE
|
app_user |
あり | なし | アプリケーション接続 |
app_rwを継承する |
app_ro |
なし | なし | 参照権限をまとめる | SELECT |
app_readonly |
あり | なし | 調査、BI、サポート |
app_roを継承する |
なぜRoleを6個も作るのか
Roleを分ける理由は、名前を増やすことではなく、権限の境界を明確にすることです。
| 起きたこと | 分離していない場合 | 分離した場合 |
|---|---|---|
| アプリにSQLインジェクションが発生 | DDLやRole管理まで影響する可能性がある | アプリ用権限の範囲へ影響を抑えやすい |
| マイグレーションに不具合がある | 日常接続と同じ資格情報でDDLが動く | マイグレーション用資格情報を停止できる |
| 調査担当へ接続を許可する | 更新操作もできてしまう | 参照専用Roleだけを付与できる |
| 担当者が増える | ユーザーごとに個別GRANTが増える | 権限Roleへの所属で管理できる |
RDSマスターユーザーは完全なSUPERUSERではない
RDS作成時の管理ユーザー名は、コンソールではデフォルトでpostgresですが、別の名前も選べます。マスターユーザー名はDBインスタンス作成後に変更できないため、運用手順では「必ずpostgres」と決めつけず、RDSの設定値を確認します。
PostgreSQLのデフォルトポートは5432です。ただし、RDS作成時に変更できるため、接続コマンドへ固定値を書き込む前に、AWSコンソールの「接続とセキュリティ」でエンドポイントとポートを確認します。
AWS公式: Connecting to a DB instance running the PostgreSQL database engine
RDS for PostgreSQLのマスターユーザーには、通常rds_superuserが付与されます。ただし、スタンドアロンPostgreSQLの完全なSUPERUSERではありません。
RDSマスターユーザー
├─ LOGIN
├─ CREATEDB
├─ CREATEROLE
├─ rds_superuser
└─ PostgreSQL本来のSUPERUSERではない
RDSはマネージドサービスです。ホストOSへのSSH接続や、本来のPostgreSQLスーパーユーザーによる操作はできません。OS管理や一部の管理処理はAWS側が担当します。
マスターユーザーの用途は、次のように限定します。
- データベースとRoleの初期作成
- 利用可能な拡張機能の導入
- 初期権限設定
- 障害調査や管理作業
- 権限を失った場合の復旧
アプリケーションの接続文字列へ、RDSマスターユーザーの資格情報を設定しません。強い資格情報が漏えいすると、アプリ用データだけでなくRoleやデータベースの管理へ影響が広がります。
SQL実行前にAWS側を確認する
Roleを作る前に、RDSへの到達経路と保護設定を確認します。データベース内の権限を細かく分けても、インターネットから広く接続できる状態では防御が不十分です。
| 項目 | デフォルトの扱い | この記事の推奨・確認内容 |
|---|---|---|
| パブリックアクセス | 作成方法と選択内容で異なる | 原則として無効。必要な場合は理由と接続元を明確にする |
| セキュリティグループ | 選択したVPCとSGで異なる | デフォルトポートは5432。実際のポートを、アプリのSG、VPN、管理経路など必要な通信元だけに許可する |
| 保存時暗号化 | RDS作成時に選択する | 現在の暗号化状態とKMSキーを確認する |
| 自動バックアップ | 作成方法や指定値で保持期間が異なる | 保持期間とポイントインタイムリカバリ要件を確認する |
| 削除保護 | 作成方法や指定値で異なる | 本番や重要環境では有効化を検討する |
| Multi-AZ | 作成方法や選択した可用性設定で異なる | 可用性要件とコストを踏まえて判断する |
| TLS強制 | PostgreSQL 15以降はrds.force_ssl=1
|
実値を確認し、クライアント側でも証明書を検証する |
| パスワード保存方式 | PostgreSQL 14以降はpassword_encryption=scram-sha-256
|
実値と既存Roleの保存方式を確認する |
| 受け入れる認証方式 | rds.accepted_password_auth_method=md5+scram |
SCRAMだけに制限する場合は、互換性確認と移行を先に行う |
| 秘密情報 | Roleを作るだけでは安全な保管先は決まらない | SQL、Git、シェル履歴へパスワードを残さない |
パブリックアクセス、バックアップ、削除保護、Multi-AZなどは、コンソールのテンプレートや作成方法によって初期選択が変わる可能性があります。「RDSのデフォルトだから有効なはず」と判断せず、対象DBインスタンスに保存されている値を確認します。
AWS公式: Settings for DB instances
セキュリティグループは送信元SGで絞る
アプリがECSやEC2上で動く場合、RDSのインバウンドルールは、可能であれば固定IPではなくアプリ側のセキュリティグループを送信元にします。
アプリのSecurity Group
↓ TCP 5432
RDSのSecurity Group
管理者が接続する場合は、VPN、踏み台、管理用VDIなど、現場で決めた経路からだけ到達できるようにします。
TLSを検証してRDSへ接続する
RDS for PostgreSQLはTLS接続に対応しています。psqlが利用するlibpqのデフォルトsslmode=preferは、まずTLSを試し、接続できなければ非TLS接続も試す設定です。また、デフォルトでは接続先ホスト名まで厳密に検証しません。接続先を確実に検証したい場合はverify-fullを明示します。
AWSが公開しているRDS用CA証明書バンドルを取得し、次のように接続します。
# 値は実際のRDSエンドポイント、管理ユーザー、証明書パスへ置き換えます。
psql "host=<RDSエンドポイント> \
port=5432 \
dbname=postgres \
user=<RDSマスターユーザー> \
sslmode=verify-full \
sslrootcert=/path/to/global-bundle.pem"
verify-fullでは、次の両方を検証します。
- 証明書が信頼できるCAから発行されているか
- 証明書のホスト名とRDSエンドポイントが一致するか
RDS for PostgreSQL 15以降では、rds.force_sslのデフォルトは1です。14以前はデフォルトが0のため、利用バージョンとDBパラメータグループを確認します。
rds.force_ssl=1はサーバー側で非TLS接続を拒否する設定です。一方、sslmode=verify-fullはクライアント側でCA証明書とホスト名を検証する設定です。役割が異なるため、RDS for PostgreSQL 15以降でもverify-fullを指定します。
接続後に確認するSQL
接続できたら、現在の接続先とTLS状態を確認します。
-- 接続ユーザー、データベース、PostgreSQLバージョンを確認します。
SELECT
current_user,
current_database(),
version();
-- 新しく設定するパスワードの暗号化方式を確認します。
SHOW password_encryption;
-- RDSがTLS接続を強制しているか確認します。
SHOW rds.force_ssl;
-- サーバーが受け入れるパスワード認証方式を確認します。
SHOW rds.accepted_password_auth_method;
-- 現在の接続で利用しているTLSバージョンと暗号スイートを確認します。
SELECT
ssl,
version,
cipher
FROM pg_stat_ssl
WHERE pid = pg_backend_pid();
想定する確認結果は次のとおりです。
| 確認項目 | 期待する状態 |
|---|---|
current_database() |
管理作業時はpostgresなど、意図したDB |
password_encryption |
本記事の対象であるRDS for PostgreSQL 15以降では、デフォルトのscram-sha-256
|
rds.force_ssl |
本記事の対象であるRDS for PostgreSQL 15以降では、デフォルトのonまたは1
|
rds.accepted_password_auth_method |
デフォルトはmd5+scram。SCRAM限定へ変更済みならscram
|
pg_stat_ssl.ssl |
trueまたはt
|
RDS for PostgreSQL 14以降では、password_encryptionのデフォルトはscram-sha-256です。ただし、この値は 今後設定・変更するパスワードの保存方式 を決めるもので、既存RoleのMD5パスワードを自動でSCRAMへ変換するものではありません。
また、rds.accepted_password_auth_methodのデフォルトはmd5+scramで、MD5とSCRAMの両方を受け入れます。SCRAMだけに変更する場合は、利用中のドライバーがSCRAMへ対応していることを確認し、対象Roleのパスワードを再設定してから切り替えます。先にSCRAM限定へ変更すると、MD5形式のパスワードしか持たないRoleが接続できなくなります。
デフォルト値を変更する場合は、通常、カスタムDBパラメータグループが必要です。変更の適用方法や再起動の要否も、対象パラメータと環境で確認します。
AWS公式: Using SCRAM for PostgreSQL password encryption
psqlの確認コマンド
次はSQLではなく、psqlのメタコマンドです。
-- Roleと属性を表示する
\du+
-- データベース一覧を表示する
\l+
-- スキーマ一覧を表示する
\dn+
-- 現在の接続情報を表示する
\conninfo
パスワードを安全に扱う
サンプルSQLへ平文パスワードを書き、Gitで管理するのは避けます。psqlを使える場合は、次のように対話入力できます。
-- 入力したパスワードは画面へ表示されません。
\password app_user
RDSは、マスターユーザーの資格情報をSecrets Managerで生成・保存・ローテーションする機能を提供しています。一方、app_userなど自分で作ったRoleは別のシークレットとして管理し、アプリの取得権限やローテーション手順を設計します。
Secrets Managerには料金がかかります。また、秘密情報を保存するだけで安全になるわけではありません。取得できるIAM Role、アプリへの渡し方、監査、ローテーション後の接続更新まで確認します。
設計レビュー用チェックリスト
- アプリケーションがマスターユーザーで接続していない
- 所有者Roleとアプリ接続Roleを分離している
- マイグレーション用Roleを通常接続から分離している
- 参照専用の利用者へ更新権限を付けていない
- RDSのパブリックアクセスとSecurity Groupを確認した
- TLS証明書とRDSエンドポイントを検証している
- パスワードをSQLファイルやGitへ保存していない
- PostgreSQLとRDSのバージョンを記録した
参考・確認先
この記事は2026年8月20日に、次の公式資料を確認して作成しました。
- AWS: Master user account privileges
- AWS: Understanding the rds_superuser role
- AWS: Using SSL with a PostgreSQL DB instance
- AWS: Using SCRAM for PostgreSQL password encryption
- AWS: Connecting to a DB instance running the PostgreSQL database engine
- AWS: Settings for DB instances
- AWS: Password management with Amazon RDS and AWS Secrets Manager
- PostgreSQL: CREATE ROLE
- PostgreSQL: Database Roles
- PostgreSQL: SSL Support
関連記事
- AWS VPC設計入門 CIDR・サブネット・ルートを最初に決める理由
- AWSインフラ工程を5ステップで整理する 要件定義・設計・実装・検証
- RDS for PostgreSQL権限設定 app_owner・app_user・app_readonlyを作る(今後公開予定)
まとめ
RDS for PostgreSQLを作成したら、最初にアプリ用の接続ユーザーを作るだけでなく、所有者、マイグレーション、読み書き、参照専用の責任を分けます。
次の記事では、この設計をCREATE ROLE、GRANT、ALTER DEFAULT PRIVILEGESへ落とし込み、データベースとスキーマを実際に構築します。
おわりに
データベースの権限設計は、Roleを作って終わりではなく、接続経路、秘密情報、検証方法まで決めておくと運用しやすくなります。
Wealthy Designでは、Webシステム開発、クラウド活用、AIを使った業務改善に取り組んでいます。
会社の取り組みは、会社サイトにまとめています。