結論から
AIツールにアプリを作らせると、データベースの「誰が読めるか」の設定(Supabase の RLS)は、動かすために切られ、動いた瞬間に戻す理由が消えます。 戻し忘れではなく、「戻す」という工程が最初から存在しないのです。
この記事で伝えたいのは、この一点だけです。
- AIは「動かない → 原因は RLS → 切る(または全員OKの設定にする)→ 動いた」の順に作業する。「動いた」の後に「誰が読めるか」を見直す工程は、頼まれない限り無い
- 結果、公開時点でテーブルが全員に読み書きできる状態になる。Moltbook(2026年1月)と CVE-2025-48757(Lovable 製アプリ1,645本中170本)は、どちらもこの状態だった
- 見る場所は Supabase の Table Editor。赤い「RLS disabled」があれば、そのテーブルは全員に公開。緑でも中身が「全員OK」の場合がある
- 直すのは「誰が読めるか」を1テーブルずつ決めること。AIへの頼み方と確かめ方を下に書いた
動画版(約2分半)は本文の上に埋め込んであります。
① AIは、何をするのか:動かすために、切る
Supabase を使ったアプリで、よくある流れです。
- あなた:「予約一覧を表示して」
- AIがコードを書く。画面は出るが、一覧が空のまま。エラーは「row-level security policy に違反」
- AIが原因を突き止める:「RLS が有効なので、読み取りがブロックされています」
- AIが直す:
ALTER TABLE reservations DISABLE ROW LEVEL SECURITY;または、RLS は残したままCREATE POLICY ... USING (true)(誰でも読める、という意味) - 一覧が出る。「修正しました。表示されるようになりました」
ここで作業が終わります。5の時点で、予約テーブルはアプリを開いた全員が読める状態です。あなたのブラウザにも、他人のブラウザにも、同じ鍵(anon キー)が配られているので、URL さえ分かればログインなしで全行を取り出せます。
② なぜ戻らないのか:「動いた」が終点だから
理由1:AIの目的は「動かすこと」で、そこで達成される。
「一覧を表示して」という依頼は、一覧が表示された時点で完了です。「誰に表示してよいか」は依頼の中に無いので、AIはそこに戻りません。次の依頼が来れば、次の機能を作りに行きます。
理由2:SQL で作ったテーブルは、最初から RLS が無効。
Supabase の管理画面(Table Editor)から作ったテーブルは RLS が有効で始まりますが、SQL(CREATE TABLE)で作ったテーブルは無効で始まります。AIツールはほぼ SQL で作るので、多くのテーブルは「切られた」のではなく「最初から開いている」のです。切っていないから、戻すという発想も生まれません。
理由3:「RLS enabled」の緑でも、中身が全員OKのことがある。
USING (true) という条件は、「全員に true(許可)」という意味です。管理画面には緑の「RLS enabled」が付き、Lovable のセキュリティスキャンも「有効」と判定しますが、守っている行は一行もありません。存在は確認されても、効果は確認されていない、という状態です。
これは特殊な失敗ではありません。2025年5月に公開された CVE-2025-48757 は、Lovable で作られたアプリのショーケース 1,645 本のうち 170 本(約10%)が、この状態のまま公開されていたと報告しています。2026年1月の Moltbook(150万件のAPIトークン)も同じ設定の話でした。作った人はどちらも「動いている」ことは確認していました。
③ どう直すのか:「誰が読めるか」を、1テーブルずつ決める
直すのは、テーブルごとに「この行は、誰が読めて、誰が書けるか」を決めることです。以下をそのまま AI ツールに貼ってください。
「Supabase の public スキーマにある全テーブルを列挙して、それぞれについて RLS が有効か、ポリシーの内容は何か、を表にしてください。RLS が無効のテーブルは有効にし、USING (true) のような誰でも読めるポリシーは、auth.uid() = user_id のように『自分の行だけ』に書き換えてください。管理者だけが読むテーブルは、is_admin フラグで判定してください。誰でも読んでよいテーブル(例:商品一覧)は、その理由を明記したうえで残してください。変更した SQL をすべて見せてください。」
ポイントは3つです。「全テーブル」と言う(AIは頼まれたテーブルしか見ない)。「USING (true) を書き換える」と言う(緑の罠を潰す)。「誰でも読んでよい理由を明記」と言う(全部閉じると、商品一覧など公開してよいものまで消える)。
④ 直ったかを確かめる:赤を探す。緑も開く
手順1:Supabase の Table Editor を開く。 左のテーブル一覧で、赤い「RLS disabled」バッジが付いたテーブルを探します。1つでもあれば、そのテーブルは全員に公開です。
手順2:緑のテーブルも、ポリシーを開く。 Authentication → Policies で、各テーブルのポリシーを見ます。USING (true) や true だけの条件があれば、②の理由3です。「商品一覧のように全員に見せてよいテーブル」以外にあれば未修正。
手順3:ログインなしで叩いてみる。 シークレットウィンドウで、https://<プロジェクト>.supabase.co/rest/v1/reservations?select=* を開きます(anon キーはアプリのコードに入っているので、F12 → Network で見えます)。全行が返ってきたら未修正、[] や 401 なら合格です。この手順が難しければ、AIに「ログインしていない状態で reservations を読み取るテストを書いて、0件になることを確認して」と頼んでください。
そして、機能を追加するたびに手順1をやり直す。AIは次の機能でも、同じ順番で作業します。
自分で直せないとき:エンジニアに送る一文
「Supabase の public スキーマで、RLS が無効のテーブルと、USING (true) になっているポリシーを全部洗い出して、テーブルごとに『誰が読めて誰が書けるか』を決めて直してください。公開してよいテーブルは理由付きで残してください。」
よくある質問
RLS を全部有効にしたら、アプリが動かなくなりました。
それが正常です。「誰も読めない」から始めて、「自分の行だけ」「管理者だけ」と必要な分だけ開けていきます。AIに「動かなくなった」と言うと、また USING (true) に戻されることがあるので、「自分の行だけ読めるポリシーを書いて」と条件を指定してください。
Lovable のセキュリティスキャンで「問題なし」でした。
スキャンは「RLS が有効か」を見ますが、「ポリシーが誰を通すか」までは見ないことがあります。緑でも②の理由3を確認してください。
service_role キーを使えば RLS は関係ないと聞きました。
servicerole キーは RLS を無視できる管理者用の鍵です。だからこそ、フロントに書いてはいけません(→ APIキーの記事)。RLS はサーバー側の判断、servicerole はサーバー側だけの鍵。両方揃って初めて守れます。
このチェックが通れば、公開して大丈夫ですか。
RLS は「データベースに直接来た人」を止める設定です。アプリの画面や API 経由の「URLの数字を変える」「/admin を開く」は別のチェックが必要です(→ 公開前5分チェック①②)。


