RLS とは何か、なぜ要るのか
Supabase は、データベースの表を、そのまま Web の API(プログラムから呼び出す窓口)として外に出せるサービスです。public という区画(スキーマ)にある表は、この API から読み書きできます。
アプリの画面は、publishable key(古いプロジェクトでは anon キー)という鍵を使って、この API を呼びます。この鍵は画面側に置く前提のもので、Supabase の説明でも Web ページやアプリに置いてよいとされています。その代わり、鍵を見た人は誰でも同じ API を呼べます。
そこで、誰がどの行を読めるかを決めるのが RLS(Row Level Security、行単位のアクセス制御)です。Supabase の公式ドキュメントは、外に出している区画の表にはすべて RLS を有効にするよう求めていて、RLS の無い表は、権限を持つ役割なら誰でも読み書きできると注意しています。
設定漏れは実際に起きています。
- Lovable で作られた1,645のアプリのうち170で、RLS の設定漏れからデータが読めた問題が、CVE-2025-48757 として登録されています。
- 2026年9月には、セキュリティ企業 UpGuard の調査で、約16,000の Supabase のデータベースで個人情報が外から読める状態だったと TechCrunch が報じました。Supabase は、守りの責任は利用者と分け合うもの(shared responsibility)だと説明しています。
なお Supabase の説明では、管理画面(Dashboard)で作った表は最初から RLS が有効になりますが、SQL エディタや別の道具で作った表は、自分で有効にする必要があります。AI が SQL で表を作った場合は、特に確かめておきたい所です。
まず鍵の種類を確かめる
鍵は Supabase の管理画面の Settings → API Keys で見られます。画面の表示は変わることがあるので、合わない場合は公式の API キーの説明を見てください。
| 鍵 | 見分け方 | 画面側に置いてよいか | RLS との関係 |
|---|---|---|---|
| publishable key | sb_publishable_ で始まる | よい | RLS に従う |
| secret key | sb_secret_ で始まる | いけない | RLS をすべて素通りする |
| anon キー(古い形式) | 管理画面で anon と表示される長い文字列 | よい | RLS に従う |
| service_role キー(古い形式) | 管理画面で service_role と表示される長い文字列 | いけない | RLS をすべて素通りする |
secret key と service_role キーが、ブラウザで動くコードやスマホのアプリに入っていたら大きな問題です。新しい secret key はブラウザから使うと拒否されますが、鍵が漏れれば別の道具から使えるので、作り直します(最後の節で説明します)。
また Supabase は、古い anon キーと service_role キーを2026年末までに廃止(deprecate)する予定だと案内しています。古い鍵を使っているなら、切り替えも計画しておきます。
Security Advisor で確かめる
いちばん手軽なのは、Supabase の管理画面にある Security Advisor(セキュリティの自動チェック)です。Advisors の中にあり、URL が /advisors/security で終わるページです。結果は ERROR、WARN、INFO の3つの段階で出ます。
RLS に関係する主な項目は次のとおりです。
| 項目名 | 段階 | 意味 |
|---|---|---|
| rls_disabled_in_public | ERROR | public の表で RLS が無効。API から読み書きできる |
| sensitive_columns_exposed | ERROR | パスワードなど機密らしい列を持つ表が、RLS 無しで API に出ている |
| security_definer_view | ERROR | ビュー(表を組み合わせた見え方)が RLS を素通りしている |
| rls_references_user_metadata | ERROR | 利用者が自分で書き換えられる情報(user_metadata)で判定している |
| permissive_rls_policy | WARN | 条件が常に成り立つポリシー(USING (true) など)がある |
| public_bucket_allows_listing | WARN | 公開のファイル置き場(Storage のバケット)の中身を一覧できる |
| policy_exists_rls_disabled | INFO | ポリシーはあるのに RLS が無効で、ポリシーが効いていない |
| rls_enabled_no_policy | INFO | RLS は有効だがポリシーが無く、API からは何も読めない |
公式の説明にもあるとおり、出た項目がすべて誤りとは限りません。商品の一覧のように、誰に見せてもよい表なら、全員が読めるのは意図どおりです。大事なのは、表ごとに「誰に見せてよいか」を自分で答えられることです。
Lovable Cloud を使っている場合
Lovable の組み込みのバックエンド(Lovable Cloud)は、Supabase をもとにしています。この場合は、Lovable のプロジェクトの Security の画面でスキャンの結果を見ます。Quick scan は、RLS の抜けや広すぎる設定を調べます。ポリシーは、Lovable の説明では More → Database → Manage → Policies で見られます。画面は変わることがあるので、公式の説明も合わせて見てください。
表とポリシーを SQL で一覧にする
Supabase の管理画面の SQL Editor では、SQL(データベースへの命令文)を実行できます。次の2つは読み取るだけの命令で、データは変わりません。
1つめは、public の表ごとに RLS が有効かどうかの一覧です。
select tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by tablename;
rowsecurity が false の表は、RLS が無効です。その表に何が入っているかを確かめてください。
2つめは、ポリシー(誰がどの行を扱えるかの決まり)の一覧です。
select tablename, policyname, roles, cmd, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename;
列の意味は次のとおりです。
roles:誰に向けた決まりか。anonはログインしていない人、authenticatedはログインしている人です。cmd:どの操作か。SELECT は読む、INSERT は追加、UPDATE は変更、DELETE は削除、ALL はすべてです。qual:どの行を対象にするかの条件です。with_check:追加や変更のときに、新しい行が満たすべき条件です。
ポリシーの読み方
よく見る条件と、その意味を並べます。
| 見かける形 | 意味 | 確かめること |
|---|---|---|
roles が anon で、qual が true | ログインしていない人も含め、誰でも全部の行を読める | 本当に公開してよいデータか |
roles が authenticated で、qual が true | ログインした人なら誰でも全部の行を読める | 他人の行まで見えてよいか |
qual が auth.uid() と user_id を比べている | ログイン中の人と、行の持ち主が同じ行だけ | user_id に正しく持ち主が入るか |
qual に user_metadata が出てくる | 利用者が自分で書き換えられる情報で判定している | Security Advisor で ERROR になる書き方 |
auth.uid() は、問い合わせてきた人の ID を返す関数です。「ログインしていれば誰でも全部読める」形は見落としやすい所です。会員だけのサービスでも、会員どうしで他人の情報が見えてよいとは限りません。
追加と変更(INSERT、UPDATE)では with_check も見ます。ここが true のままだと、他人の user_id を付けた行も追加できます。
外から試して確かめる
一覧で問題が無さそうでも、実際に試すのが確実です。ここでの試し方は、自分のプロジェクトにだけ行ってください。
アプリの画面で試す
テスト用のアカウントを2つ作り、片方で作ったデータが、もう片方やログアウトした状態から見えないかを確かめます。手順は「AIで作ったアプリの公開前チェック」にあります。
鍵だけで API を呼んでみる
画面を通さず、publishable key だけで表を読めるかを試します。ログインしていない第三者が、画面から鍵を拾って問い合わせた場合と同じ立場です。
- プロジェクトの URL を、管理画面の Integrations → Data API で確かめます。
https://<プロジェクトID>.supabase.coの形です。 - publishable key を、Settings → API Keys で確かめます。
- ターミナル(Mac の「ターミナル」など、コマンドを打つ画面)で、次を実行します。
<>の部分は自分の値に置き換えます。
curl "https://<project-ref>.supabase.co/rest/v1/<表の名前>?select=*&limit=1" \
-H "apikey: <publishable key>"
limit=1 は、返ってくる行を1件までにする指定です。結果は次のように読みます。
[](空の一覧):ログインしていない人には、その表の行が1件も見えていません。RLS で隠れているか、表が空です。- データが返る:ログインしていない人でも、その表を読めます。公開してよいデータでなければ、すぐに直します。
- エラーが返る:鍵や表の名前の誤りか、その表が API から届かない設定になっています。
Supabase の説明では、publishable key は apikey という欄(ヘッダー)で送ります。publishable key はログインしていない人(anon)の立場で動き、古い anon キーはその前身なので、アプリが古い鍵を使っていても、この確認で足ります。
見つかったときの直し方
RLS を有効にして、ポリシーを作る
RLS が無効の表は、次の命令で有効にします。
alter table public.<表の名前> enable row level security;
RLS を有効にしてポリシーが無いと、API からは何も読めなくなります。アプリの画面が空になることがあるので、続けてポリシーを作ります。次は「ログインした人が、自分の行だけ読める」ポリシーの例で、Supabase の公式の例と同じ形です。
create policy "自分の行だけ読める"
on public.todos for select
to authenticated
using ( (select auth.uid()) = user_id );
AI に直させるときは、表ごとに「誰が、どの操作をしてよいか」を書いた表を渡すと、意図とずれにくくなります。
ビューとファイル置き場
ビューは、標準では RLS を素通りします。Postgres 15 以降なら、ビューに security_invoker = true を付けると、元の表の RLS が効きます。ファイル置き場(Storage)は、Security Advisor の public_bucket_allows_listing が出ていないかを見ます。
鍵や個人情報が漏れていた場合
secret key や service_role キーが外に出ていた場合は、漏れた原因を直してから鍵を作り直します。手順は Supabase の「漏れた API キーの作り直し」の説明に沿ってください。古い service_role キーの場合は、古い鍵の無効化に加えて、JWT secret の取り消しまで必要です。
個人情報が外から読める期間があった場合、報告や本人への連絡が要るかどうかは、個人情報保護委員会の「漏えい等の対応」の案内を確かめ、弁護士に相談してください。
直したら、Security Advisor と外からの確認をもう一度行います。判断に迷う所は、経験のある人に一度見てもらうと早いです。