本文へスキップ

Supabase RLSを有効にする方法と結果

Markdownで表示Claudeで開くChatGPTで開くPerplexityで開く

質問もコピーされます。チャットが空で開いた場合は貼り付けてください。

TL;DR

SupabaseでRLSを有効にするには、テーブルごとに文を1つ実行します。alter table public.todos enable row level security;、またはダッシュボードの「Enable RLS」です。SQLエディタやマイグレーションで作ったテーブルは、デフォルトでは有効になりません。有効にした直後、テーブルはエラーを返すのではなく、静かになります。ローカルでの私のテストでは、public keyと、ログイン済みの2人のユーザーは、select、update、deleteで200 []を受け取り、失敗したのはinsertだけで42501でした。secret keyは3行すべてを見たままでした。テーブルの上に通常のビューを作ると、public keyに3行すべてが渡ったままでした。2026年10月30日からは、新しいテーブルは何かが届く前にgrantも必要になるため、grantと同じマイグレーションでRLSを有効にし、ポリシーを書いてください。

私は無料のSupabaseセキュリティチェッカーを運営しています。その中で「RLS disabled」は、最も説明が不十分だと感じる指摘です。多くのガイドは、RLSを有効にすると「テーブルにアクセスできなくなる」と書いています。読み取りについては正しく、書き込みについては誤解を招きます。そこで、ローカルのSupabaseスタックに1つのテーブルを作り、下の各ステップのあとに、public key、ログイン済みの2人のユーザー、secret keyとして、あらゆる種類のリクエストを送りました。

SupabaseのRLSとは?

RLS(row-level security、行レベルセキュリティ)は、リクエストごとに行を絞り込むPostgresの機能です。Supabaseはsupabase-jsからのすべてのリクエストを、ロールとして実行されるデータベースクエリに変換します。public(anon、publishable)keyならanon、ログイン済みのユーザーならauthenticatedです。RLSが有効だと、Postgresは行ごとにポリシーを確認し、ポリシーが許可した行だけを返し、変更し、削除します。RLSが無効だと、テーブルにgrantを持つロールは、すべての行に届きます。

secret key(service role)は別です。そのロールはBYPASSRLSを持つため、RLSは決して適用されません。サーバー上にだけ置くべき理由です。2つのキーの違いは、Supabase anon keyとservice role keyの違い:何が開くかにあります。

SupabaseでRLSを有効にするには?

テーブルの作り方によって、3つの方法があります。

  • Table Editorでテーブルを作るときは、「Enable Row Level Security (RLS)」にチェックを入れたままにします。Supabaseのダッシュボードのソースでは「Recommended」と表示され、外すと、テーブルが誰にでも読み書きできる状態になるという警告が出ます。
  • 既存のテーブルについては、SupabaseのAPIセキュリティのガイドに、Database、Policiesの順に進み、テーブルの「Enable RLS」を選ぶとあります。
  • SQLで、SQLエディタまたはマイグレーションに書きます。
alter table public.todos enable row level security;

Supabaseのガイドは明確に述べています。ダッシュボードで作ったテーブルはデフォルトでRLSが有効で、SQLエディタや別のツールで作ったテーブルはそうではありません。マイグレーションやAIツールでスキーマを作っているなら、すべてのテーブルを確認してください。

RLSを有効にした直後はどうなる?

insert以外はエラーになりません。所有者の列(user_id uuid default auth.uid())を持つpublic.todosを作り、古いプロジェクトと同じようにAPIのロールにgrantして、3行を追加しました。ユーザーAの2行とユーザーBの1行です。そのあと、ポリシーなしでRLSを有効にしました。

リクエスト public key ユーザーA ユーザーB secret key
Select 200、0行 200、0行 200、0行 200、3行
自分の行のInsert 401、エラー 42501 403、エラー 42501 403、エラー 42501 201、行を挿入
行のUpdate 200、0行 200、0行 200、0行 200、1行
行のDelete 200、0行 200、0行 200、0行 200、1行

insertのエラーはnew row violates row-level security policyです。updateとdeleteは空の配列を付けて200を返すため、返された行を確認しないアプリは、何も保存されていないのに「保存しました」と報告します。RLSを有効にしたあとは、書き込んだ内容を読み戻すか、Prefer: count=exactで件数を取得してください。

RLSの前は、同じgrantのままで、public keyは3行すべてを読み、insertでき、任意の行をupdateやdeleteできました。ユーザーAはユーザーBの行を編集・削除でき、自分の行をBに渡すこともできました。

テーブルをまた使えるようにするには?

操作ごとにポリシーを書きます。自分の行用の一般的なセットは次のとおりです。

create policy "own select" on public.todos for select to authenticated
  using ((select auth.uid()) = user_id);
create policy "own insert" on public.todos for insert to authenticated
  with check ((select auth.uid()) = user_id);
create policy "own update" on public.todos for update to authenticated
  using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id);
create policy "own delete" on public.todos for delete to authenticated
  using ((select auth.uid()) = user_id);

これで、ユーザーAは自分の2行、ユーザーBは自分の1行が見えました。public keyは0行のままです。AがBの所有する行をinsertしようとしたとき、または自分の行をBに移そうとしたときの応答は403, 42501でした。この拒否は、updateのwith checkによるものです。AがBの行をupdateまたはdeleteしようとしたときの応答は200, 0 rowsでした。ほかのパターン(公開プロフィール、チーム、MFA)は、Supabase RLSポリシー例:APIで検証済みにあります。

secret keyについて1つ細かな点があります。secret keyが挿入した行はuser_id = nullになりました。そのキーの背後にはユーザーがおらず、auth.uid()が何も返さないためです。RLSは迂回されましたが、デフォルト値は列名が示唆する動きをしませんでした。

各ステップでpublic keyは何を読めた?

public keyが読めた行数(3行中)。3行のSupabaseテーブルを、各ステップのあとに確認。新規、grantなし、RLS無効(42501):0、grantあり、RLS無効:3、RLS有効、ポリシーなし:0、RLS有効、自分の行のポリシー:0、通常のビュー:3、security_invokerのビュー:0。
図1. ローカルのSupabaseスタック(CLI 2.109.1、Postgres 17.6)。2026年10月28日。

すべてが漏れるステップが2つあります。RLSのないgrantと、正しいポリシーを持つテーブルの上の通常のビューです。次の2つのセクションで、その両方を説明します。

10月30日のgrant変更のあとも、RLSは必要?

必要です。2026年10月30日から、Supabaseはpublicの新しいテーブルに、APIのロールへの自動grantを付けなくなりました。新規プロジェクトも既存プロジェクトも同じです。私のローカルスタックは、すでにその動作でした。RLSが無効の状態で作ったばかりのテーブルは、secret keyを含むすべてのロールに42501を返したため、何も漏れませんでした。詳細はSupabase 10月30日のgrant変更:壊れる点と確認方法にあります。

ただし、アプリがテーブルを必要とした時点でgrantは戻ってきます。そこからは、public keyとすべての行の間にあるのはRLSだけです。そこで、すべてを1つのマイグレーションに、この順番で入れてください。

create table public.todos (...);
alter table public.todos enable row level security;
create policy "own select" on public.todos ...;
grant select, insert, update, delete on table public.todos to authenticated;

grantは、そのテーブルが必要なロールだけに与えます。anonからすべてを取り消すと、public keyのselectは200 []から401, 42501に変わりました。grantが1つ目の鍵、RLSが2つ目の鍵です。

ビューではなぜ行が漏れる?

ビューは所有者の権限で実行され、Supabaseでは所有者がpostgresで、RLSを迂回するからです。todosに自分の行のポリシーを設定したうえで、create view public.todos_view as select * from public.todosという通常のビューを作ると、public keyとユーザーBに3行すべてが返りました。

with (security_invoker = true)で作り直すと、同じビューがテーブルとまったく同じ結果になりました。public keyには0行、Aには2行、Bには1行です。

create view public.todos_view with (security_invoker = true) as
  select * from public.todos;

SupabaseのAdvisorは、通常の種類をsecurity_definer_viewとして警告します。その意味と直し方は、Supabase security definer viewの解説ノートにあります。

新しいテーブルすべてでRLSを自動的に有効にするには?

イベントトリガーを使います。Supabaseのイベントトリガーのガイドには、関数rls_auto_enable()と、publicでのcreate table、create table as、select intoのあとに実行されるトリガーensure_rlsが載っています。公開されているとおりに入れたところ、次の結果になりました。

  • そのあとにcreate tableで作ったテーブルは、alterなしでRLSが有効でした。public keyはselectで200 []、insertで401でした。
  • create table as selectで作ったテーブルも、RLSが有効でした。
  • トリガーより前に作ったテーブルは、無効のままでした。トリガーはさかのぼって適用されません。

トリガーがないと、保護されたテーブルの隣に作った2つ目のテーブルはRLSが無効で、grantすればpublic keyが自由に読み書きできました。また、対象はpublicだけです。公開している他のスキーマには、関数に専用の行が必要です。

FORCE ROW LEVEL SECURITYは役に立つ?

心配なロールには効きません。alter table ... force row level securityは、RLSをテーブルの所有者にも適用させます。Supabaseではpostgresもservice_roleもBYPASSRLSを持つため、forceのあとも所有者は3行を数え、secret keyはすべてを見たままで、postgresが所有するビューは漏れたままでした。変化があったのは、BYPASSRLSなしで私が作った所有者ロールだけで、以前は2行、以後は0行でした。

自分のプロジェクトをどう確認する?

  • SQLエディタで、RLSが無効なpublicのテーブルを一覧します。
select c.relname as table_name
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
  and c.relkind in ('r', 'p')
  and not c.relrowsecurity
order by 1;
  • ダッシュボードのAdvisorは、同じ内容をrls_disabled_in_publicとして報告します。このエラーの意味はRLS disabled in publicの解説ノートにあります。
  • 無料のSupabaseセキュリティチェックは、プロジェクトを読み取り専用で調べ、APIが届くRLS無効のテーブル、RLSを迂回するビュー、全員に許可するポリシーを、それぞれの修正SQL付きで一覧します。
  • スキャナーは、どの行が誰のものかを知ることができません。それを証明するには、上のように2人のユーザーとしてリクエストを送ります。ロールのあるアプリの完全な方法は、Supabase RLSのテスト:87権限中30が無効だったにあります。

テスト方法

  • ノートPC上のSupabase CLI 2.109.1:Postgres 17.6、PostgREST 14.14、Auth 2.192.0。パスワードでログインした2人のユーザーと、ローカルのpublishable keyおよびsecret key。
  • テーブルはpublic.todosの1つで、リクエストのたびに同じ3行に戻しました。各ステップはSQLとして適用し、各リクエストはREST API経由で送り、ステータスコードと本文を記録しました。
  • ステップ2〜5のgrantは、2026年10月30日より前に作られたプロジェクトに合わせて、手作業で追加しました。

限界。 ローカルスタック1つ、架空のデータ、2026年10月28日です。ダッシュボードの手順はSupabaseのドキュメントからの引用で、実際にクリックして確認したものではありません。ダッシュボード自身のロールで作ったテーブルは、別のデフォルトgrantになる可能性があります。それも、Storage、Realtime、GraphQLも、テストしていません。

テーブルが守れているか不安ですか?

スキーマを一緒に見ることができれば嬉しいです。売り込みはありません。

お問い合わせ

© 2026 Denys Zaichenko. 元の記事: https://madsalt.tech/ja/blog/how-to-enable-rls-in-supabase/