本文へスキップ

Supabase「RLS disabled in public」とは? 意味と直し方

Supabase

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

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

TL;DR

「RLS Disabled in Public」(lint 0013_rls_disabled_in_public、レベルはERROR)は、公開されたスキーマのテーブルでrow level security(RLS)がオフなのに、anonまたはauthenticatedがselectできる状態を指します。私のローカルのテストでは、公開キーはそのようなテーブルの3行中3行すべてを読み、201 Createdで新しい行も挿入しました。RLSを有効にし、操作ごとにポリシーを追加し、grantを確認する作業を、1つのマイグレーションで行ってください。

私は無料のSupabaseセキュリティチェッカーを作りましたが、その最初のルール「公開されたテーブルで RLS が無効」は、SupabaseのSecurity Advisorがエラーとして報告するものと同じ指摘です。AIツールで作られたアプリで私が最もよく見つける穴なので、これが何を意味するかと、再現して何が可能だったかを説明します。

「RLS disabled in public」とは何か?

Security Advisorは、rls_disabled_in_publicというlintを実行します。そのSQLは、次の3つが同時に成り立つときにテーブルを指摘します。

  1. row level securityがオフの、通常のテーブルである。
  2. anonまたはauthenticatedが、そのテーブルにSELECTを持っている。
  3. スキーマが、Data APIのExposed schemasリストにある。デフォルトはpublicです。

lintのページは、これをERRORとし、インターネット経由での作成・読み取り・更新・削除がすべて可能な状態を「a critically unsafe configuration」(極めて危険な設定)と呼んでいます。名前の「public」はスキーマのことですが、lintは公開されているすべてのスキーマを対象にします。だから、そのリストにある余分なスキーマも確認する価値があります。これはSupabase Exposed schemasに書きました。

公開キーはそのテーブルに何ができる?

ローカルのSupabaseスタック(CLI 2.109.1、Postgres 17.6、PostgREST 14.14)に、偽の顧客3人のordersテーブルを作りました。既存プロジェクトが新しいテーブルに付けるgrantを持たせ、RLSはオフです。そのうえで、ブラウザと同じようにpublishable keyでData APIを呼び出しました。

1つのテーブル、公開キー、RLSオフとオン。ローカルのSupabase、偽の注文3行。RLSオフ、読み取り:Publishable key → orders、RLSオフ → 3行中3行、RLSオフ、挿入:Publishable key → orders、RLSオフ → 201 Created、RLSオン、ポリシーはまだなし:Publishable key → orders、RLSオン → 0行。
ローカルのSupabaseスタックでの私の再現、2026年10月23日。
publishable keyでのリクエスト RLSオフ RLSオン、ポリシーなし
GET /rest/v1/orders?select=* 200、名前とメールを含む3行すべて 200、[]
新しい行をPOST /rest/v1/orders 201 Created 401、42501 “new row violates row-level security policy”

アプリを開いた人は誰でも、ページからpublishable keyをコピーできます。RLSがオフなら、そのキーはこのテーブルへの読み書きの通行証になります。

アプリを壊さずに直すには?

RLSを有効にするだけだと、まだポリシーがないので、テーブルはアプリに何も返さなくなります。この空の画面のときに、人も、AIツールも、using (true)のポリシーを足してしまいます。それは、すべての行を全員に返します。そのあと更新や削除が、何も変えないのに成功と報告する理由はSupabase RLSを有効にする方法と結果にあります。本当のポリシーは、同じマイグレーションの中で書いてください。

alter table public.orders enable row level security;

revoke all on table public.orders from anon, authenticated;
grant select, insert on table public.orders to authenticated;

create policy "Customers read their orders"
on public.orders for select
to authenticated
using ((select auth.uid()) = user_id);

create policy "Customers create their orders"
on public.orders for insert
to authenticated
with check ((select auth.uid()) = user_id);

公開記事の一覧のように、テーブルを公開する場合でも、RLSはオンのままにして、anon向けのselectポリシーを1つ書き、書き込みのgrantは付けません。Data APIを使わず、サーバーからだけPostgresに接続するなら、Supabaseのメンテナーが同じissueで提案したように、Exposed schemasからpublicを外してください。

新しいテーブルがRLSなしで始まらないようにするには、Supabaseのevent triggersのガイドに、publicに作られるすべてのテーブルでRLSを有効にするトリガーがあります。また、2026年10月30日からは、新しいテーブルにData APIのgrantがデフォルトで付かなくなり、grantするまで42501を返します。これはSupabase 42501 permission deniedで扱いました。

問題なしという結果は、何を証明しない?

RLSがオンであることは、ポリシーが正しいかどうかについて何も教えてくれません。同じテストで、using (true)と書いたポリシーは、公開キーに再び3行すべてを返し、RLSがオンになるとこのlintは何も言わなくなります。チェッカーには、そのための別のルール「すべての行を許可するポリシー」があり、Supabase RLSのテスト方法では、実在のユーザーで各ポリシーを証明する方法を示しています。出発点にできるポリシーを、実在のユーザーでAPI経由で実行した結果とともに、Supabase RLSポリシー例:APIで検証済みにまとめました。

© 2026 Denys Zaichenko. 元の記事: https://madsalt.tech/ja/notes/supabase-rls-disabled-in-public/