Supabase の RLS 無効化は本当に危険か?anon key の露出リスクと現実的な対処フロー
Supabaseからセキュリティ警告メールが来た。RLSを無効にした状態でNEXT_PUBLICにanon keyを置くと何が起きるか。
Supabaseからメールが届いた。件名は「Your project has security issues」。開いたら「Row Level Security (RLS) が無効になっているテーブルがあります」という警告だった。
警告の意味
RLS(Row Level Security)はPostgreSQLの行単位アクセス制御機能だ。Supabaseで有効にすると、テーブルへのアクセスに対してポリシーを設定でき、「このユーザーはこの行だけ読める」といった制御ができる。
RLSを無効にすると、テーブルへのアクセス制御はSupabaseのAPIキーのレベルだけに依存する。
anon key が NEXT_PUBLIC に置いてある場合の問題
Next.jsのフロントエンドから直接Supabaseにアクセスする構成では、NEXT_PUBLIC_SUPABASE_ANON_KEY という環境変数にanon keyを置くのが一般的だ。NEXT_PUBLIC_ プレフィックスがついた環境変数はブラウザに公開される。つまり、サイトにアクセスした人であれば誰でもanon keyを取得できる。
Supabaseのanon keyは「匿名ユーザーとしてAPIを叩くためのキー」だ。これ自体は公開されることを想定して設計されている。問題はその組み合わせだ。
- 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"
どんな場合に問題で、どんな場合に問題でないか
問題になるケース:
- 本番サービスでユーザーデータやセンシティブなデータを保持している
- 書き込みを想定していないデータに誰でも書ける状態
- テーブルが実質公開状態になって困る内容を持っている
意図的にOKなケース:
- 開発・実験用のプロジェクトで外部公開しない
- テーブルの内容が本来パブリックなデータ(公開してよい情報のみ)
- サーバーサイドのみでAPIを叩き、フロントにanon keyを置いていない
開発段階でRLSを後回しにしている場合、Supabaseは警告メールを送ってくる。警告メール自体は「危険な状態」を教えてくれているだけで、即座にデータが漏れているわけではない。ただし、そのプロジェクトをそのままデプロイして公開すると問題になる。
対処フロー
パターンA:フロントから直接Supabaseにアクセスしている場合
RLSを有効化し、ポリシーを設定する。最低限のポリシーは:
-- 認証済みユーザーは自分のデータだけ読める
CREATE POLICY "Users can read own data"
ON public.users
FOR SELECT
USING (auth.uid() = id);
ポリシーを設定せずにRLSだけ有効にすると、全アクセスが拒否されるので注意。
パターンB:サーバーサイドのみでアクセスしている場合
Next.jsのServer ComponentsやRoute Handlerからのみアクセスし、フロントにanon keyを置かない構成なら、RLS無効でも実害は小さい。フロントに 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はブラウザに公開されない。
パターンC:開発専用で外部公開しない
ローカル開発や検証専用であれば、Supabaseの警告は無視して先に進んでよい。本番公開前に必ずRLSの設計を見直す。
まとめ
- anon keyの公開 + RLS無効 = テーブルが事実上パブリック
- anon keyはそもそも公開を想定した設計なので、問題はRLS無効の方にある
- フロントから直接Supabaseにアクセスする構成ならRLSは必須
- サーバーサイドのみでアクセスするなら
NEXT_PUBLIC_を使わずservice_roleキーで完結させる方が設計として明快
※ 本記事にはアフィリエイトリンクが含まれます。