テック技報最新号へ →
コード

Postgres: RLS のポリシーはアプリが使うロールに切り替えてテストする

スーパーユーザーや BYPASSRLS を持つロールは行レベルセキュリティ(RLS)を素通りするので、SQL エディタで「動いた」ことはポリシーが正しい証拠にならない。SET ROLE でアプリと同じロールになってから確かめる。

@james_ailton · 2026-11-11
#postgres#rls#supabase#security#testing

使う場面

Supabase や自前の Postgres で RLS を使ってテナント分離・本人のデータだけ見せる制御をしているとき。管理画面の SQL エディタは postgres(スーパーユーザー)で動くことが多く、ポリシーが一つもなくても全行が見えてしまう。

RLS を素通りするのはスーパーユーザーだけではない。

  • BYPASSRLS 属性を持つロール
  • テーブルの所有者(ALTER TABLE ... FORCE ROW LEVEL SECURITY を付けない限り)

マイグレーションを流したロールがそのままアプリの接続ロールになっていると、本番でも RLS が効いていないことがある。

やり方

トランザクションの中でロールと JWT のクレームを切り替え、最後に ROLLBACK で戻す(Supabase の例)。

sql
BEGIN;
SET LOCAL ROLE authenticated;
-- auth.uid() などが読む値をセット(第3引数 true でこのトランザクション内だけ)
SELECT set_config(
  'request.jwt.claims',
  '{"sub":"00000000-0000-0000-0000-000000000001","role":"authenticated"}',
  true
);

SELECT * FROM documents;            -- 本人の行だけ返るか
UPDATE documents SET title = 'x'
  WHERE owner_id <> auth.uid();     -- 0 行になるか
ROLLBACK;

未ログインの状態は SET LOCAL ROLE anon; で同じように確かめる。

自前の Postgres なら、アプリ用ロールを所有者と分けて作り、テストでも SET ROLE app_user; してからクエリを流す。所有者のまま使う必要があるなら FORCE ROW LEVEL SECURITY を付ける。

注意点

  • SET ROLE で切り替えられるのは、今のロールがそのロールのメンバーであるとき(スーパーユーザーなら可)。
  • SET LOCAL はトランザクションが終われば元に戻る。BEGIN を忘れると、接続を使い回すプールで設定が残ることがある。
  • 確かめたい項目(他人の行が見えない・書けない・未ログインで 0 行)を SQL のテストにして CI で流すと、ポリシーの書き換えで穴が開いたときに気づける(Supabase なら pgTAP が使える)。

出典

X の元の投稿を開く