はじめに
個人開発でSupabaseを使ってToDoアプリを作っていたある日、こんな件名のメールが届きました。
Security vulnerabilities detected in your Supabase projects
(Supabaseプロジェクトでセキュリティ脆弱性が検出されました)
怖っ!!いったい何でしょうか。
中身を読んでみると、自分のプロジェクトにCritical(重大)な問題が2つあるとのこと。
原因は2つともRLS(Row-Level Security)が有効になっていないことでした。
対応を備忘録として残しておきます。
届いたメールの内容
メールに書かれていた問題は2つです。
① Table publicly accessible(テーブルが公開状態)
Anyone with your project URL can read, edit, and delete all data in this table because Row-Level Security is not enabled.
つまり、プロジェクトのURLさえ分かれば、誰でもテーブルのデータを読み書き・削除できる状態になっていますよ、という警告です。
② Sensitive data publicly accessible(機密データが公開状態)
A table with columns that likely contain sensitive data (like passwords or personal identifiers) is accessible through the API without any access restrictions.
テーブルのカラム名から「パスワードや個人情報が入ってそうだな」とSupabaseが判断して、それがアクセス制限なしで公開されていることを警告しています。
どちらも原因は同じで、RLSが有効になっていないことが問題のようです。
RLS とは?
RLS(Row-Level Security)は、テーブルの行単位でアクセス制御をかける仕組みです。
Supabaseでは、フロントエンドからデータベースにアクセスするために anon key というAPIキーを使います。このキーは、基本的にはJavaScriptのコードに直接書くものなので、ブラウザの開発者ツールを開けば誰でも見えます。
つまり、RLSが無効だと:
- anon keyはフロントエンドのコードから誰でも取得できる
- そのキーを使えばAPIを直接叩ける
- テーブルのデータを好き放題読み書きできる
という状態になります。テスト用のプロジェクトだったとはいえ、なかなかゾッとしますね。
ちなみに今回の自分のプロジェクトでは、フロントエンドから直接Supabaseを叩く構成ではなく、バックエンドAPI経由でアクセスしていたため、anon keyはフロントに露出していませんでした。それでもRLSが無効というだけでSupabaseから警告が飛んできます。「自分はバックエンド経由だから大丈夫」と思っていても、RLSは多層防御として設定しておくのが安全です。
なお、SupabaseのダッシュボードのGUI(New Table)からテーブルを作る場合は、現在デフォルトでRLSが有効になっています。しかし、SQL Editorで CREATE TABLE を直接実行した場合は、明示的に ALTER TABLE テーブル名 ENABLE ROW LEVEL SECURITY; を書かない限りRLSは無効のままです。今回の自分のケースもこれで、マイグレーションSQLでテーブルを作っていたためRLSが無効になっていました。
対応方法
やることはシンプルで、RLSを有効にして、ポリシーを設定するだけです。
ステップ1:RLSを有効にする
Supabaseダッシュボードにログインして、対象のテーブルを開きます。
テーブルの上部に「RLS disabled」のような警告バナーが表示されているはずです。ここから「Enable RLS」をクリックすれば有効化できます。
ステップ2:ポリシーを設定する
RLSを有効にしただけでは、誰もデータにアクセスできない状態になります。「全部ブロック」がデフォルトです。なので、「誰が・何を・できるか」をポリシーとして定義する必要があります。
たとえば「ログインしたユーザーが自分のデータだけ読み書きできる」ポリシーはこんな感じです:
-- SELECT(読み取り)のポリシー
CREATE POLICY "ユーザーは自分のデータだけ読める"
ON public.todos
FOR SELECT
USING (auth.uid() = user_id);
-- INSERT(追加)のポリシー
CREATE POLICY "ユーザーは自分のデータだけ追加できる"
ON public.todos
FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- UPDATE(更新)のポリシー
CREATE POLICY "ユーザーは自分のデータだけ更新できる"
ON public.todos
FOR UPDATE
USING (auth.uid() = user_id);
-- DELETE(削除)のポリシー
CREATE POLICY "ユーザーは自分のデータだけ削除できる"
ON public.todos
FOR DELETE
USING (auth.uid() = user_id);
auth.uid() は、現在ログインしているユーザーのIDを返すSupabaseの関数です。テーブルの user_id カラムと一致する行だけにアクセスを許可しています。
ダッシュボードのGUIからもポリシーは作成できるので、SQLが苦手な方はそちらからでも大丈夫です。
ステップ3:確認
設定が終わったら、ダッシュボードでRLSが有効になっていること、ポリシーが正しく設定されていることを確認しましょう。
まとめ
- ダッシュボードのGUIからテーブルを作ればRLSはデフォルトで有効だが、SQLで
CREATE TABLEすると RLSは無効のまま - RLSが無効 = anon keyさえあれば誰でもデータにアクセスできる
-
anon keyはフロントエンドに露出するものなので、RLSが実質的なアクセス制御になる - テーブルを作ったらすぐにRLSを有効化してポリシーを設定するのを習慣にする
Supabaseは本当に便利で、個人開発の強い味方です。ただ、便利さの裏で「デフォルトが安全とは限らない」ということを今回身をもって学びました。同じ警告メールが届いた方は、テスト環境だからと放置せずにサクッと対応しましょう。




