0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Supabaseから怖いメールが来た話 ~Row-Level Securityとは~

0
Last updated at Posted at 2026-05-28

はじめに

個人開発でSupabaseを使ってToDoアプリを作っていたある日、こんな件名のメールが届きました。

Security vulnerabilities detected in your Supabase projects
(Supabaseプロジェクトでセキュリティ脆弱性が検出されました)

怖っ!!いったい何でしょうか。

中身を読んでみると、自分のプロジェクトにCritical(重大)な問題が2つあるとのこと。

原因は2つともRLS(Row-Level Security)が有効になっていないことでした。
対応を備忘録として残しておきます。

届いたメールの内容

メールに書かれていた問題は2つです。

image.png

① 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(機密データが公開状態)

image.png

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が無効だと:

  1. anon keyはフロントエンドのコードから誰でも取得できる
  2. そのキーを使えばAPIを直接叩ける
  3. テーブルのデータを好き放題読み書きできる

という状態になります。テスト用のプロジェクトだったとはいえ、なかなかゾッとしますね。

ちなみに今回の自分のプロジェクトでは、フロントエンドから直接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ダッシュボードにログインして、対象のテーブルを開きます。

image.png

テーブルの上部に「RLS disabled」のような警告バナーが表示されているはずです。ここから「Enable RLS」をクリックすれば有効化できます。

image.png

ステップ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が有効になっていること、ポリシーが正しく設定されていることを確認しましょう。

image.png

まとめ

  • ダッシュボードのGUIからテーブルを作ればRLSはデフォルトで有効だが、SQLでCREATE TABLEすると RLSは無効のまま
  • RLSが無効 = anon keyさえあれば誰でもデータにアクセスできる
  • anon key はフロントエンドに露出するものなので、RLSが実質的なアクセス制御になる
  • テーブルを作ったらすぐにRLSを有効化してポリシーを設定するのを習慣にする

Supabaseは本当に便利で、個人開発の強い味方です。ただ、便利さの裏で「デフォルトが安全とは限らない」ということを今回身をもって学びました。同じ警告メールが届いた方は、テスト環境だからと放置せずにサクッと対応しましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?