PostgreSQL セキュリティガイド
このドキュメントでは、PostgreSQLのセキュリティ設定とベストプラクティスを解説します。
目次
1. セキュリティの全体像
1.1 多層防御
┌──────────────────────────────────────────────────────────────┐
│ PostgreSQL セキュリティ層 │
├──────────────────────────────────────────────────── ──────────┤
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 1: ネットワーク │ │
│ │ - ファイアウォール │ │
│ │ - listen_addresses │ │
│ │ - SSL/TLS暗号化 │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 2: 認証 (Authentication) │ │
│ │ - pg_hba.conf │ │
│ │ - パスワード (scram-sha-256) │ │
│ │ - 証明書、LDAP、GSSAPI等 │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 3: 認可 (Authorization) │ │
│ │ - ロールと権限 (GRANT/REVOKE) │ │
│ │ - スキーマ分離 │ │
│ │ - Row Level Security (RLS) │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Layer 4: 監査と監視 │ │
│ │ - ログ設定 │ │
│ │ - 監査拡張 (pgAudit) │ │
│ │ - 異常検知 │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
1.2 最小権限の原則
┌──────────────────────────────────────────────────────────────┐
│ 最小権限の原則 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 【原則】 │
│ 各ユーザー/アプリケーションには、 │
│ 必要最小限の権限のみを付与する │
│ │
│ 【悪い例】 │
│ - アプリケーションがスーパーユーザーで接続 │
│ - 全テーブルへのフルアクセス権限 │
│ - publicスキーマに全オブジェクトを配置 │
│ │
│ 【良い例】 │
│ - 用途別にロールを分離 │
│ - app_read: SELECT のみ │
│ - app_write: SELECT, INSERT, UPDATE, DELETE │
│ - app_admin: 上記 + DDL │
│ - スキーマで論理的に分離 │
│ - Row Level Security で行単位のアクセス制御 │
│ │
└──────────────────────────────────────────────────────────────┘
2. 認証 (Authentication)
2.1 pg_hba.conf の構 造
┌──────────────────────────────────────────────────────────────┐
│ pg_hba.conf の形式 │
├──────────────────────────────────────────────────────────────┤
│ │
│ TYPE DATABASE USER ADDRESS METHOD [OPTIONS] │
│ ──── ──────── ──── ─────── ────── ───────── │
│ │
│ TYPE: │
│ - local : Unix ドメインソケット │
│ - host : TCP/IP (SSL/非SSL 両方) │
│ - hostssl : SSL 接続のみ │
│ - hostnossl: 非SSL 接続のみ │
│ - hostgssenc: GSSAPI 暗号化接続のみ │
│ │
│ DATABASE: │
│ - all : 全データベース │
│ - sameuser : ユーザー名と同じ名前のDB │
│ - samerole : ユーザーが属するロール名と同じDB │
│ - replication : レプリケーション接続 │
│ - dbname : 特定のデータベース名 │
│ │
│ USER: │
│ - all : 全ユーザー │
│ - +rolename : ロールのメンバー │
│ - username : 特定のユーザー名 │
│ │
│ ADDRESS: │
│ - CIDR表記 : 192.168.1.0/24 │
│ - ホスト名 : client.example.com │
│ - all : 全アドレス │
│ │
└──────────────────────────────────────────────────────────────┘
2.2 認証方式の比較
┌──────────────────────────────────────────────────────────────┐
│ 認証方式の比較 │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────┬───────────────────────────────────┐ │
│ │ 方式 │ 説明 │ │
│ ├───────────────────┼───────────────────────────────────┤ │
│ │ trust │ 無条件で許可 (危険!) │ │
│ │ reject │ 無条件で拒否 │ │
│ │ scram-sha-256 │ パスワード認証 (推奨) │ │
│ │ md5 │ MD5パスワード (非推奨) │ │
│ │ password │ 平文パスワード (危険!) │ │
│ │ peer │ OSユーザー名で認証 (localのみ) │ │
│ │ ident │ Identプロトコルで認証 │ │
│ │ cert │ SSL証明書で認証 │ │
│ │ ldap │ LDAPサーバーで認証 │ │
│ │ radius │ RADIUSサーバーで認証 │ │
│ │ gss │ GSSAPI/Kerberos認証 │ │
│ └───────────────────┴───────────────────────────────────┘ │
│ │
│ 【推奨】 │
│ - ローカル接続: peer (postgres OSユーザー) │
│ - リモート接続: scram-sha-256 + SSL │
│ - エンタープライズ: LDAP または cert │
│ │
└────────── ────────────────────────────────────────────────────┘
2.3 本番環境向け pg_hba.conf 例
# /var/lib/pgsql/16/data/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
# ローカル接続 (postgres スーパーユーザー)
local all postgres peer
# ローカル接続 (一般ユーザー)
local all all scram-sha-256
# ローカルホストからのTCP接続
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
# アプリケーションサーバーからの接続 (SSL必須)
hostssl myapp app_user 10.0.1.0/24 scram-sha-256
hostssl myapp app_readonly 10.0.1.0/24 scram-sha-256
# 管理者からの接続 (特定IPのみ、SSL必須)
hostssl all admin_user 10.0.100.10/32 scram-sha-256
# レプリケーション (SSL必須)
hostssl replication repl_user 10.0.2.0/24 scram-sha-256
# その他は全て拒否
host all all 0.0.0.0/0 reject
host all all ::/0 reject
2.4 パスワード管理
-- パスワードの設定 (scram-sha-256)
ALTER USER myuser WITH PASSWORD 'secure_password_here';
-- パスワード有効期限の設定
ALTER USER myuser VALID UNTIL '2025-12-31';
-- 接続制限
ALTER USER myuser CONNECTION LIMIT 10;
-- パスワードの暗号化方式を確認
SHOW password_encryption; -- scram-sha-256
-- ユーザーのパスワード情報確認
SELECT usename, passwd IS NOT NULL AS has_password, valuntil
FROM pg_shadow;
# postgresql.conf
password_encryption = 'scram-sha-256' # デフォルトの暗号化方式
2.5 LDAP認証の設定
# pg_hba.conf (LDAP認証)
host all all 10.0.0.0/8 ldap ldapserver=ldap.example.com ldapbasedn="dc=example,dc=com" ldapsearchattribute=uid
# Simple Bind方式
host all all 10.0.0.0/8 ldap ldapserver=ldap.example.com ldapprefix="uid=" ldapsuffix=",ou=users,dc=example,dc=com"
# Search+Bind方式 (より柔軟)
host all all 10.0.0.0/8 ldap ldapserver=ldap.example.com ldapbasedn="ou=users,dc=example,dc=com" ldapsearchattribute=uid ldapbinddn="cn=pgbind,dc=example,dc=com" ldapbindpasswd="bindpass"
3. ロールと権限
3.1 ロールの基本
┌──────────────────────────────────────────────────────────────┐
│ ロールの概念 │
├──────────────────────────────────────────────────────────────┤
│ │
│ PostgreSQLでは「ユーザー」と「グループ」の区別がない │
│ 全て「ロール」として統一的に管理 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ admin_role │ │
│ │ (ログイン不可) │ │
│ └────────────────────────┬────────────────────────────┘ │
│ │ GRANT admin_role TO ... │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌──────── ───┐ ┌───────────┐ │
│ │ alice │ │ bob │ │ charlie │ │
│ │(ログイン可)│ │(ログイン可)│ │(ログイン可)│ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
│ 【ロールの属性】 │
│ - LOGIN: ログイン可能 │
│ - SUPERUSER: スーパーユーザー │
│ - CREATEDB: データベース作成可能 │
│ - CREATEROLE: ロール作成可能 │
│ - REPLICATION: レプリケーション可能 │
│ - INHERIT: メンバーシップの権限を継承 │
│ │
└──────────────────────────────────────────────────────────────┘
3.2 ロールの作成と管理
-- ログイン可能なユーザー作成
CREATE ROLE app_user WITH
LOGIN
PASSWORD 'secure_password'
CONNECTION LIMIT 50
VALID UNTIL '2025-12-31';
-- グループロール作成 (ログイン不可)
CREATE ROLE app_readers;
CREATE ROLE app_writers;
CREATE ROLE app_admins;
-- メンバーシップの付与
GRANT app_readers TO app_user;
GRANT app_writers TO app_user;
-- 管理者ロール
CREATE ROLE db_admin WITH
LOGIN
CREATEDB
CREATEROLE
PASSWORD 'admin_password';
-- ロールの確認
\du
SELECT * FROM pg_roles;
-- ロールの削除 (所有オブジェクトの移管が必要)
REASSIGN OWNED BY old_user TO new_user;
DROP OWNED BY old_user;
DROP ROLE old_user;
3.3 権限の種類
┌──────────────────────────────────────────────────────────────┐
│ 権限の種類 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 【オブジェクトレベル権限】 │
│ │
│ ┌─────────────────┬───────────────────────────────────┐ │
│ │ 権限 │ 対象オブジェクト │ │
│ ├─────────────────┼───────────────────────────────────┤ │
│ │ SELECT │ テーブル, ビュー, シーケンス │ │
│ │ INSERT │ テーブル │ │
│ │ UPDATE │ テーブル, シーケンス │ │
│ │ DELETE │ テーブル │ │
│ │ TRUNCATE │ テーブル │ │
│ │ REFERENCES │ テーブル │ │
│ │ TRIGGER │ テーブル │ │
│ │ CREATE │ データベース, スキーマ │ │
│ │ CONNECT │ データベース │ │
│ │ TEMPORARY │ データベース │ │
│ │ EXECUTE │ 関数, プロシージャ │ │
│ │ USAGE │ スキーマ, シーケンス, 型 │ │
│ └─────────────────┴───────────────────────────────────┘ │
│ │
│ 【特殊権限】 │
│ - ALL PRIVILEGES: 全ての権限 │
│ - WITH GRANT OPTION: 権限を他者に付与可能 │
│ │
└──────────────────────────────────────────────────────────────┘
3.4 権限の付与と取り消し
-- データベースへの接続権限
GRANT CONNECT ON DATABASE myapp TO app_user;
-- スキーマへのアクセス権限
GRANT USAGE ON SCHEMA app TO app_readers;
GRANT CREATE ON SCHEMA app TO app_admins;
-- テーブルへの権限
GRANT SELECT ON TABLE app.users TO app_readers;
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE app.users TO app_writers;
GRANT ALL PRIVILEGES ON TABLE app.users TO app_admins;
-- スキーマ内の全テーブルへの権限
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_readers;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_writers;
-- 今後作成されるテーブルへのデフォルト権限
ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT SELECT ON TABLES TO app_readers;
ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_writers;
-- シーケンスへの権限
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_writers;
ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT USAGE, SELECT ON SEQUENCES TO app_writers;
-- 関数への権限
GRANT EXECUTE ON FUNCTION app.my_function() TO app_users;
-- 権限の取り消し
REVOKE INSERT, UPDATE, DELETE ON TABLE app.users FROM app_readers;
-- 権限の確認
\dp app.users
SELECT * FROM information_schema.table_privileges
WHERE table_schema = 'app' AND table_name = 'users';
3.5 public スキーマのセキュリティ
-- デフォルトではpublicスキーマに誰でもオブジェクトを作成できる
-- これはセキュリティリスク
-- public スキーマからの CREATE 権限を削除
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
-- public スキーマの USAGE 権限を削除 (より厳格)
REVOKE ALL ON SCHEMA public FROM PUBLIC;
-- 特定のロールにのみ許可
GRANT USAGE ON SCHEMA public TO app_users;
3.6 推奨されるロール設計
-- 1. アプリケーション用ロール階層
-- 読み取り専用ロール
CREATE ROLE app_readonly;
GRANT CONNECT ON DATABASE myapp TO app_readonly;
GRANT USAGE ON SCHEMA app TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT SELECT ON TABLES TO app_readonly;
-- 読み書きロール
CREATE ROLE app_readwrite;
GRANT app_readonly TO app_readwrite; -- 読み取り権限を継承
GRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_readwrite;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_readwrite;
ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT INSERT, UPDATE, DELETE ON TABLES TO app_readwrite;
ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT USAGE, SELECT ON SEQUENCES TO app_readwrite;
-- 管理者ロール
CREATE ROLE app_admin;
GRANT app_readwrite TO app_admin;
GRANT CREATE ON SCHEMA app TO app_admin;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA app TO app_admin;
-- 2. 実際のユーザーにロールを付与
CREATE ROLE web_app WITH LOGIN PASSWORD 'xxx';
GRANT app_readwrite TO web_app;
CREATE ROLE batch_app WITH LOGIN PASSWORD 'xxx';
GRANT app_readwrite TO batch_app;
CREATE ROLE report_app WITH LOGIN PASSWORD 'xxx';
GRANT app_readonly TO report_app;
4. Row Level Security (RLS)
4.1 RLSの概念
┌──────────────────────────────────────────────────────────────┐
│ Row Level Security (RLS) │
├──────────────────────────────────────────────────────────────┤
│ │
│ 通常の権限: テーブル全体へのアクセス制御 │
│ RLS: 行単位でのアクセス制御 │
│ │
│ 【使用例】 │
│ - マルチテナントアプリケーション │
│ - 部署ごとのデータ分離 │
│ - ユーザーごとに自分のデータのみ表示 │
│ │
│ ┌── ────────────────────────────────────────────────────┐ │
│ │ orders テーブル │ │
│ │ ┌────────┬──────────┬─────────┬─────────┐ │ │
│ │ │ id │ tenant_id│ user_id │ amount │ │ │
│ │ ├────────┼──────────┼─────────┼─────────┤ │ │
│ │ │ 1 │ A │ alice │ 1000 │ ← tenant A│ │
│ │ │ 2 │ A │ bob │ 2000 │ ← tenant A│ │
│ │ │ 3 │ B │ charlie │ 3000 │ ← tenant B│ │
│ │ │ 4 │ B │ dave │ 4000 │ ← tenant B│ │
│ │ └────────┴──────────┴─────────┴─────────┘ │ │
│ └─── ───────────────────────────────────────────────────┘ │
│ │
│ tenant A のユーザーが SELECT * FROM orders を実行 │
│ → id 1, 2 のみ表示 (tenant_id = 'A' の行のみ) │
│ │
└──────────────────────────────────────────────────────────────┘
4.2 RLSの基本設定
-- 1. テーブルでRLSを有効化
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- 2. ポリシーの作成
-- SELECT用ポリシー
CREATE POLICY orders_tenant_isolation ON orders
FOR SELECT
USING (tenant_id = current_setting('app.current_tenant'));
-- INSERT用ポリシー
CREATE POLICY orders_tenant_insert ON orders
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.current_tenant'));
-- UPDATE用ポリシー
CREATE POLICY orders_tenant_update ON orders
FOR UPDATE
USING (tenant_id = current_setting('app.current_tenant'))
WITH CHECK (tenant_id = current_setting('app.current_tenant'));
-- DELETE用ポリシー
CREATE POLICY orders_tenant_delete ON orders
FOR DELETE
USING (tenant_id = current_setting('app.current_tenant'));
-- 全操作に対するポリシー (ALL)
CREATE POLICY orders_tenant_all ON orders
FOR ALL
USING (tenant_id = current_setting('app.current_tenant'))
WITH CHECK (tenant_id = current_setting('app.current_tenant'));
4.3 アプリケーションからの使用
-- アプリケーションセッション開始時にテナントを設定
SET app.current_tenant = 'tenant_a';
-- 以降のクエリは自動的にフィルタリング
SELECT * FROM orders; -- tenant_a の行のみ返る
-- トランザクション内で設定
BEGIN;
SET LOCAL app.current_tenant = 'tenant_a';
SELECT * FROM orders;
COMMIT;
4.4 ユーザーベースのRLS
-- ユーザーが自分のデータのみアクセス可能
CREATE POLICY user_isolation ON user_data
FOR ALL
USING (user_id = current_user)
WITH CHECK (user_id = current_user);
-- または SESSION_USER を使用
CREATE POLICY user_isolation ON user_data
FOR ALL
USING (user_id = SESSION_USER);