行レベルセキュリティとは?
PostgreSQLのRLS(Row Level Security)=行レベルセキュリティは、
マルチテナントSaaSやCRMのようにユーザーごとに見せるデータを制御したいときに必須の仕組みです。
RLSは、テーブル単位で「行ごとにアクセス制御」をかける仕組みです。
通常のPostgreSQLでは、
SELECT * FROM customers;
を実行すると全行が見えますが、RLSを有効にすると「自分の行」だけしか見えなくできます。
どういうときに使うか?
シナリオ | RLSで解決できること |
---|---|
SaaSアプリ | 各テナントのデータを分離(他社データを見れない) |
社内CRM | 営業担当ごとに顧客データを制限 |
AIログ | 組織ごとにログアクセスを制御 |
基本の仕組み
- テーブル単位でRLSを有効化
- POLICY(ポリシー)を設定して、誰がどの行を見られるか定義
- PostgreSQLが自動で
WHERE
句を追加してくれる
ポリシーの設定例
-- サンプルテーブル作成
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name TEXT,
org_id TEXT,
email TEXT
);
-- RLSを有効化
ALTER TABLE customers ENABLE ROW LEVEL SECURITY;
-- ポリシーを作成
-- customersテーブルの org_id カラムが、PostgreSQLセッション変数 app.current_org の値と一致する行だけを見せる
-- ::textはテキストにキャストしている
CREATE POLICY org_isolation_policy
ON customers
USING (org_id = current_setting('app.current_org')::text);
動作確認
-- アプリ側で「現在の組織ID」をセットする
SET app.current_org = 'org_abc';
-- クエリーを実行すると、org_id = org_abcのみのレコードが返される
SELECT * FROM customers;
-- PostgreSQL内部で自動的に以下に変換されている
SELECT * FROM customers WHERE org_id = 'org_abc';
RLSの種類(SELECT / INSERT / UPDATE / DELETE別)
操作ごとに設定できる
-- SELECT制限
CREATE POLICY select_own_org
ON customers
FOR SELECT
USING (org_id = current_setting('app.current_org')::text);
-- INSERT制限
CREATE POLICY insert_own_org
ON customers
FOR INSERT
WITH CHECK (org_id = current_setting('app.current_org')::text);
RLSの強制適用
以下により、superuserやテーブル所有者であっても、RLSポリシーが強制的に適用されます。
LTER TABLE customers FORCE ROW LEVEL SECURITY;
Python(psycopg2)の例
cur.execute("SET app.current_org = %s;", (org_id,))
cur.execute("SELECT * FROM customers;")
Node(pg)の例
await client.query("SET app.current_org = $1", [org_id]);
await client.query("SELECT * FROM customers");
応用例
階層によってスコープを分離できる
目的 | 実装例 |
---|---|
organization単位 | current_setting('app.current_org') |
workspace単位 | current_setting('app.current_workspace') |
project単位 | current_setting('app.current_project') |
user単位 | current_setting('app.current_user') |