AIのあとしまつ

実務・技術解説

Supabase RLS(行レベルセキュリティ)設定ガイド——AIコードで起きやすい穴とその直し方

Supabase RLSを正しく設定しないと全データが公開状態になる。AIコーディングで特に見落とされやすいRLSの落とし穴を、実際のSQLと一緒に解説する。

Supabase RLSSupabase RLS 設定行レベルセキュリティSupabase セキュリティRLS ポリシー 設定Supabase 本番 設定Supabase auth.uidSupabase 認証 セキュリティAIコーディング Supabaseデータ漏洩 防止

Supabaseを使ったアプリで本番公開前に確認すべきことは多いが、RLS(Row Level Security)の設定ミスは最も深刻なリスクの一つだ。

これが「起きた場合」の話ではない。2026年2月、AIエージェント専用SNS「Moltbook」でSupabaseの権限設定ミスにより、認証なしで誰でもアクセスできる状態のデータベースが外部に公開された。流出したのは約150万件のAPIキー、3.5万件超のメールアドレス、そして数千件のプライベートメッセージだ。「本来ユーザー単位で制限されるべきデータベース閲覧が全公開になっていた」とZennの解析記事で報告されている。

RLSが未設定または設定が甘い場合、何が起きるか。Supabaseのanon key(フロントエンドのコードに含まれている)があれば、誰でもデータベースの全データを読み書きできる状態になる。ブラウザのコンソールを開いて数行のJavaScriptを実行するだけで、全ユーザーのデータが抜き出せる。

AIが生成するコードは「まず動かす」ことを優先するため、RLSは「開発中は無効」「全許可で後で直す」という状態で放置されやすい。「後で直す」が本番公開前に実行されないのが現実だ。Moltbookのケースはその典型だ。

RLSの仕組みを理解する

Supabaseは通常のWebアプリと違い、クライアント(ブラウザ)からデータベースに直接アクセスできる。これが便利さの源泉だが、「誰がどのデータにアクセスできるか」をデータベース自体で制御しないといけない。それがRLSだ。

RLSが無効のテーブルは「鍵のかかっていない倉庫」と同じだ。URLとキーを知っている人なら誰でも入れる。

-- テーブルのRLSを確認する(Supabase SQL Editorで実行)
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public';
-- rowsecurity が false のテーブルは全公開状態
-- RLSを有効化する
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- ポリシーを設定する(自分のデータだけ読める)
CREATE POLICY "own_data_only"
  ON posts FOR SELECT
  USING (auth.uid() = user_id);

auth.uid() はSupabaseが提供する関数で、現在ログインしているユーザーのIDを返す。これをユーザーIDのカラムと一致させることで、「自分のデータしか見えない」制御が実現する。

よくある落とし穴と直し方

落とし穴1: RLSが無効のまま

最も単純で最も危険なケースだ。Moltbookのインシデントはこれにあたる。

Supabaseのダッシュボードでテーブルを作ると、デフォルトではRLSが無効だ。AIが「開発しやすいように」無効のまま作業を続け、そのまま本番に出るパターンがある。

確認方法: Supabaseダッシュボード → Table Editor → テーブルを選択 → 「RLS disabled」と表示されていないか確認する。または上記のSQLで全テーブルを確認する。

修正: 公開するすべてのテーブルで ENABLE ROW LEVEL SECURITY を実行してポリシーを設定する。

落とし穴2: 全許可ポリシーが残っている

開発中の「とりあえず動かす」ために全許可ポリシーが作られ、本番にそのまま持ち込まれるケース。

-- これは「鍵をかけているように見えて鍵がかかっていない」状態
CREATE POLICY "allow_all"
  ON posts FOR ALL
  USING (true)
  WITH CHECK (true);

USING (true) は「誰でもOK」という意味だ。RLSが有効でもこのポリシーでは意味がない。

確認方法: Supabaseダッシュボード → Authentication → Policies でポリシーの一覧を確認する。USING (true) のポリシーが残っていないかチェックする。

修正: USING (auth.uid() = user_id) のように、実際のユーザーIDで制限するポリシーに置き換える。

落とし穴3: SELECT だけポリシーがある(INSERT/UPDATE/DELETEがない)

読み取りのポリシーだけ設定して、書き込み系の操作を忘れるケースだ。

-- INSERT にポリシーがない場合、デフォルトで「拒否」される
-- しかし全許可ポリシーを設定した場合は全員が書き込める
CREATE POLICY "allow_insert"
  ON posts FOR INSERT
  WITH CHECK (auth.uid() = user_id); -- 自分のuser_idでしかINSERTできない

CRUD(CREATE/READ/UPDATE/DELETE)すべての操作にポリシーが必要だ。操作ごとに「誰が」「どの条件で」できるかを設定する。

操作RLSキーワード意味
SELECT(読み取り)FOR SELECT USING (...)誰が読めるか
INSERT(作成)FOR INSERT WITH CHECK (...)誰が作れるか
UPDATE(更新)FOR UPDATE USING (...) WITH CHECK (...)誰が更新できるか
DELETE(削除)FOR DELETE USING (...)誰が削除できるか

落とし穴4: service role key がフロントエンドに

Supabaseには2種類のキーがある。

キー用途RLS
anon keyフロントエンドで使用RLSが適用される
service role keyサーバーサイドでのみ使用RLSをバイパスする

service role key はRLSを無視してすべてのデータにアクセスできる管理者キーだ。これがフロントエンドのコードに含まれていると、誰でも全データにアクセスできる。

AIが「RLSで弾かれて動かない」問題を解決するために、service role key を使うコードを生成することがある。

確認方法: コード内で SERVICE_ROLEsupabaseAdmin を検索する。これがブラウザで実行されるコードにある場合は即刻修正が必要だ。

修正: service role key はサーバーサイド(Next.jsのAPI Routes、Edge Functions)でのみ使用する。NEXT_PUBLIC_ のプレフィックスは付けない。

落とし穴5: 管理者ロールの設計ミス

管理者が全データを操作できるようにするとき、設計を間違えると一般ユーザーが管理者権限を取得できてしまう。

-- 危険なパターン(usersテーブルのroleカラムを信頼している)
CREATE POLICY "admins_can_read_all"
  ON posts FOR SELECT
  USING (
    (SELECT role FROM users WHERE id = auth.uid()) = 'admin'
  );

このポリシー自体は正しく見えるが、users.role カラムをユーザー自身が更新できる場合、誰でも自分を管理者にできてしまう。

安全なパターン: 管理者ロールの付与はサービスロールキーを使ったサーバーサイドの処理でのみ行う。または、JWTのカスタムクレームにロール情報を埋め込む。

本番前RLSチェックリスト

[ ] 全てのpublicテーブルでRLSが有効になっているか
[ ] USING (true) や WITH CHECK (true) の全許可ポリシーが残っていないか
[ ] SELECT/INSERT/UPDATE/DELETEの4操作すべてにポリシーがあるか
[ ] service role key がフロントエンドのコードに含まれていないか
[ ] 管理者ロールの付与が一般ユーザーにできない設計になっているか
[ ] 開発中に作った「テスト用のゆるいポリシー」が残っていないか

外部から今すぐチェックしたい場合は? アプリのURLを入力するだけでデータ露出リスクを自動診断できるSupabaseセキュリティ診断を無料公開しています。テーブルごとの読み書き権限と機密情報の露出を確認できます。

Supabaseダッシュボードでの確認方法

  1. Supabaseダッシュボードにログイン
  2. 対象プロジェクトを選択
  3. 左メニューから「Authentication」→「Policies」を開く
  4. テーブルごとのRLS有効/無効とポリシー一覧を確認できる

「RLS disabled」と表示されているテーブルが公開データなら問題ないが、ユーザーデータが入るテーブルは必ずRLSを有効にする。


RLSは設定さえ正しければ強力なセキュリティ機構だ。ただし、AIコーディングで作ったアプリはこの設定が甘くなりやすい。本番公開前の確認項目として、必ずチェックしてほしい。RLS以外のセキュリティリスクについてはAI生成コードのセキュリティリスク10選を、非エンジニア向けの対策まとめはVibe Codingで最低限やるべきセキュリティ対策を参照。

RLSの設定が正しいか不安な場合は、AIのあとしまつのセキュリティ診断で確認できる。コードとSupabaseプロジェクトを見て、問題箇所を特定して修正する。