概要
PostgreSQLでは、GRANTベースのアクセス制御(テーブルや列レベル)に加え、**ユーザー単位で特定の行の可視性や更新可否を制御する仕組み(行レベルのアクセス制御)**としてRow Level Security(RLS)が提供されている。
RLSの基本概念
Row Level Securityは、テーブルに対して「ポリシー」を定義することで機能する。
ポリシーは、どのユーザーがどの行にアクセスできるかを決定するルールを定義する。
RLSを有効にする方法
デフォルトでは、RLSは無効であるため、有効にする必要がある。
有効にすると明示的に許可された行以外は不可視/更新不可になる。
有効な状態でテーブルにポリシーが存在しない場合は、デフォルト拒否で不可視/更新不可になる。
スーパーユーザーやBYPASSRLS属性を持つユーザーはRLSを無視するが、ALTER TABLE ... FORCE ROW LEVEL SECURITYでテーブルの所有者もRLSを有効にできる。
一意性制約・外部キー制約などの参照整合性制約はRLSを無視する。
RLSが有効な状態でバックアップするには、row_securityをoffにしてデータ欠損を防止する必要がある。
ポリシーの定義
基本構文
ポリシーはCREATE POLICYで定義する。
- policy_name: ポリシーの名前(テーブルごとに一意)
- PERMISSIVE | RESTRICTIVE: ポリシーの種類(後述)
- FOR: ポリシーの適用コマンド(ALLがデフォルト)
- TO: 対象ロール(デフォルトは PUBLIC 全ユーザー)
- USING: 既存行アクセス許可条件(boolean式)
- WITH CHECK: 新規挿入・更新行の検査条件(boolean式)
USING式は次のような意味を持つ。
- SELECT・UPDATE・DELETE時に、ユーザがアクセスできる行を絞るフィルター
- 式が true の行だけがユーザに「見える」かつ操作対象となる
- false または null の場合は行は見えず操作不可(エラーは出ない)
WITH CHECK式は次のような意味を持つ。
- INSERT・UPDATE時に「新しくテーブルに追加・更新される行」の妥当性検査
- true なら操作成功、false または null ならエラーで操作拒否
- 挿入・更新前に実行される(BEFOREトリガの後)
ポリシーの種類
ポリシーには2種類ある。
- PERMISSIVE(許容ポリシー)
- 複数の許容ポリシーは論理和(OR)で結合される
- いずれかの許容ポリシーを満たせばアクセス可能
- デフォルトは許容ポリシー
- RESTRICTIVE(制限ポリシー)
- 複数の制限ポリシーは論理積(AND)で結合される
- すべての制限ポリシーを満たさなければならない
許容ポリシーが1つもなければアクセス不可になる。
コマンド別ポリシー適用の特徴
| コマンド | USING式の役割 | WITH CHECK式の役割 | 備考 |
|---|---|---|---|
| SELECT | 可視化される行の選択条件 | 不使用 | SELECT権限が必要 |
| INSERT | 不使用 | 挿入行の検査条件 | 挿入時の検査にのみ使用 |
| UPDATE | 更新対象行の選択条件 | 更新後の行検査条件 | SELECTも必要になる場合が多い |
| DELETE | 削除対象行の選択条件 | 不使用 | SELECT権限が必要 |
| ALL | SELECT/UPDATE/DELETEすべて | INSERT/UPDATEの検査条件 | すべてのコマンドに適用される |
ALLポリシーは他のコマンドポリシーと組み合わせて適用される。
SELECTポリシーは挿入・更新対象ではないため、WITH_CHECK式を持てない。
実践的な使用例
ユースケース
RLSを使ったユースケースには例えば以下のようなものがある。
1. マルチテナントSaaS
ユースケース: 複数の顧客(テナント)が同じテーブルを共有しているが、他の顧客のデータは見られないようにしたい。
事例: SaaSサービスで users テーブルに複数の会社(company_id)に属するユーザーが存在。ログインユーザーの company_id に基づいて、自社データのみを読み書きできるようにする。
2. ユーザー別のデータ可視性
ユースケース: ユーザーごとにアクセス可能なデータが異なる(例:個人用ToDoリストやメモアプリ)。
事例: todos テーブルで、ユーザー自身が作成したToDoしか見られないようにする。
3. 部門や役職ごとのデータアクセス制御
ユースケース: 人事評価システムなどで、部門長は自部門のメンバー情報を見られるが、他部門の情報は非表示にしたい。
事例: employees テーブルに department_id があり、ユーザーごとに閲覧可能な部門IDリストをセッション変数にセット。
4. 法的・規制要件への対応(監査、機密保持)
ユースケース: 金融や医療業界などで、特定の条件を満たしたユーザーのみにアクセスを許可する。
事例: 医療データベースで、医師は自分の担当患者のカルテのみを閲覧可能。
ハンズオン
ユースケースに合わせてRLSのハンズオンをする。
環境構築
docker compose up -dで起動する。
動作検証
aliceユーザー(company_id=1)でusersを閲覧する。
bobユーザーでtodosを閲覧(current_user = 'bob')する。
hr_managerユーザーでemployeesを閲覧(複数部門を許可)する。
doctorユーザーでmedical_recordsを閲覧(担当患者のみ)
まとめ
PostgreSQLのRow Level Security(RLS)は、データベースにおけるきめ細かな行レベルのアクセス制御を実現する機能である。
- RLSはテーブルに対してポリシーを定義することで機能し、ユーザーが特定の行にアクセスできるかどうかを制御
- RLSは
ALTER TABLE テーブル名 ENABLE ROW LEVEL SECURITYによって有効化 - ポリシーは
CREATE POLICYステートメントで作成し、USING句とWITH CHECK句で条件を指定 - PERMISSIVE(OR結合)とRESTRICTIVE(AND結合)の2種類のポリシータイプがある
current_userやcurrent_settingなどのコンテキスト関数と組み合わせて柔軟なポリシーを作成可能- マルチテナントSaaS、ユーザー別データ分離、部門別アクセス制御、規制コンプライアンスなど様々なユースケースに適用可能