PostgreSQL – 行レベルセキュリティポリシー(RLS)の利用

その他

行レベルセキュリティとは?

PostgreSQLのRLS(Row Level Security)=行レベルセキュリティは、
マルチテナントSaaSやCRMのようにユーザーごとに見せるデータを制御したいときに必須の仕組みです。

RLSは、テーブル単位で「行ごとにアクセス制御」をかける仕組みです。

通常のPostgreSQLでは、

SELECT * FROM customers;

を実行すると全行が見えますが、RLSを有効にすると「自分の行」だけしか見えなくできます。

どういうときに使うか?

シナリオRLSで解決できること
SaaSアプリ各テナントのデータを分離(他社データを見れない)
社内CRM営業担当ごとに顧客データを制限
AIログ組織ごとにログアクセスを制御

基本の仕組み

  1. テーブル単位でRLSを有効化
  2. POLICY(ポリシー)を設定して、誰がどの行を見られるか定義
  3. 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')

関連記事

カテゴリー

アーカイブ

Lang »