実務・技術解説
Supabase RLS(行レベルセキュリティ)設定ガイド——AIコードで起きやすい穴とその直し方
Supabase RLSを正しく設定しないと全データが公開状態になる。AIコーディングで特に見落とされやすいRLSの落とし穴を、実際のSQLと一緒に解説する。
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_ROLE や supabaseAdmin を検索する。これがブラウザで実行されるコードにある場合は即刻修正が必要だ。
修正: 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ダッシュボードでの確認方法
- Supabaseダッシュボードにログイン
- 対象プロジェクトを選択
- 左メニューから「Authentication」→「Policies」を開く
- テーブルごとのRLS有効/無効とポリシー一覧を確認できる
「RLS disabled」と表示されているテーブルが公開データなら問題ないが、ユーザーデータが入るテーブルは必ずRLSを有効にする。
RLSは設定さえ正しければ強力なセキュリティ機構だ。ただし、AIコーディングで作ったアプリはこの設定が甘くなりやすい。本番公開前の確認項目として、必ずチェックしてほしい。RLS以外のセキュリティリスクについてはAI生成コードのセキュリティリスク10選を、非エンジニア向けの対策まとめはVibe Codingで最低限やるべきセキュリティ対策を参照。
RLSの設定が正しいか不安な場合は、AIのあとしまつのセキュリティ診断で確認できる。コードとSupabaseプロジェクトを見て、問題箇所を特定して修正する。