Supabase の RLS 無効化は本当に危険か?anon key の露出リスクと現実的な対処フロー
「Your project has security issues」というRLS警告メール。anon key公開 × RLS無効で何が起きるか、構成別の対処。
Supabaseから「Row Level Security (RLS) が無効なテーブルがあります」という警告メールが届く。これが実際に危険なのか、構成別に整理する。
事象
- Supabaseから件名「Your project has security issues」のメールが届く
- 内容は「RLSが無効になっているテーブルがある」という警告
- 即座にデータが漏れているわけではないが、放置してデプロイすると問題になる
原因
危険になるのは anon key の公開 と RLS無効 が重なったときだ。
RLS(Row Level Security)はPostgreSQLの行単位アクセス制御。無効にすると、テーブルへのアクセス制御はAPIキーのレベルだけに依存する。
一方、フロントから直接Supabaseを叩く構成では NEXT_PUBLIC_SUPABASE_ANON_KEY に anon key を置く。NEXT_PUBLIC_ 付きの環境変数はブラウザに露出するので、サイトを開いた人なら誰でも anon key を取得できる。
anon key 自体は公開前提で設計されているので、それだけなら問題ない。問題は組み合わせだ。
- anon key が公開されている(誰でも取得できる)
- RLS が無効(行レベルの制御がない)
この2つが重なると、anon key を入手した人が service_role キーなしでテーブルを自由に読み書きできる。
# anon keyだけあれば誰でもこれが動く
curl 'https://xxxxxxxxx.supabase.co/rest/v1/users?select=*' \
-H "apikey: eyJxxxxxx" \
-H "Authorization: Bearer eyJxxxxxx"
解決策
構成によって対処が変わる。
パターンA:フロントから直接Supabaseにアクセスしている
RLSを有効化し、ポリシーを設定する。
-- 認証済みユーザーは自分のデータだけ読める
CREATE POLICY "Users can read own data"
ON public.users
FOR SELECT
USING (auth.uid() = id);
⚠️ ポリシーを設定せずRLSだけ有効にすると全アクセスが拒否されるので注意。
パターンB:サーバーサイドのみでアクセスしている
Server ComponentsやRoute Handlerからのみアクセスし、フロントにキーを置かない。NEXT_PUBLIC_ を使わず service_role キーで操作する。
// server-side only
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY! // NEXT_PUBLIC_ なし
)
anon key がブラウザに露出しないので、RLS無効でも実害は小さい。
パターンC:開発専用で外部公開しない
ローカル検証専用なら警告は無視して進めてよい。ただし本番公開前に必ずRLS設計を見直す。
補足:問題になるケースの見分け
| 危険 | 問題なし |
|---|---|
| 本番でユーザーデータ等を保持 | 開発・実験用で非公開 |
| 書き込み想定外のデータに誰でも書ける | 内容が本来パブリックなデータのみ |
| 公開されると困る内容を持つ | サーバーサイドのみでanon keyを露出していない |
要点:anon keyの公開そのものは設計通り。直すべきはRLS無効の方。フロント直アクセスならRLSは必須、サーバー経由なら service_role で完結させる方が設計として明快。