Supabaseで会員制コミュニティを作るときの安全チェックリスト|RLS・Storage・管理者権限・退会
Supabase と Next.js で会員制のコミュニティを作るとき、画面が動いた後に確かめておきたい項目をチェックリストにしました。RLS・関数・Storage・認証・管理者権限・退会・鍵の扱いまで、公開前の見直しにお使いください。
Supabase は、ブラウザから直接データベースに問い合わせられるのが大きな利点です。その代わり、権限の設定を1か所でも誤ると、ログインした利用者が他人のデータを読めたり、書き換えられたりします。AI にコードを書いてもらう場合は、画面が期待どおりに動いていても、この部分が抜けていることがあります。
以下は、コミュニティSNS(プロフィール・投稿・返信・画像・通報・ブロック・管理画面がある構成)を想定したチェックリストです。
先に結論
- public スキーマのすべてのテーブルで RLS を有効にし、既定の権限を見直す
- ブロック・利用停止・管理者の判定は、画面ではなくデータベースの関数とポリシーで行う
- Storage は非公開にし、読み書きをポリシーで制限する
- 「他人として操作したら失敗する」ことを、テストで確かめる
この記事の目次
前提: Supabase では「データベースが最後の砦」
Supabase の公開用の鍵(anon / publishable key)は、ブラウザに配る前提の鍵です。つまり、利用者は画面を通さずに、その鍵で直接データベースの API を呼べます。画面でボタンを隠しても、API を直接呼ばれれば意味がありません。
そのため、「この人はこの行を読める・書ける」という判断は、すべてデータベースの RLS(行ごとのアクセス制限)とポリシーで行う必要があります。
1. テーブルと RLS
- public スキーマのすべてのテーブルで RLS が有効か:新しく足したテーブルで有効にし忘れていないかを、Supabase の Security Advisor でも確かめます
- 不要な権限を取り消しているか:
anon・authenticatedに既定で付く権限を見直し、必要な操作と列だけにgrantします - insert と update の
with check:owner_id = auth.uid()を確かめないと、他人になりすました行を作れてしまいます - 利用者に書き換えさせない列:
role・is_hidden・created_atなどは、列単位のgrantで書き換えられないようにします - ブロックの反映:ブロックした・された相手の投稿やプロフィールが、読み取りのポリシーで見えなくなっているか
- 利用停止の反映:利用停止にした利用者が、投稿・返信・通報を作れなくなっているか
2. 関数・ビュー・トリガー
- SECURITY DEFINER の関数:作成者の権限で動くため、
search_pathを固定し、引数の利用者が本人かを関数の中で確かめます。public スキーマに置くと API から呼べる点にも注意します - ビュー:既定では作成者の権限で動き、元のテーブルの RLS を通らないことがあります。
security_invokerを付けるか、公開しないスキーマに置きます - 判定の関数を1か所に:「ブロックしているか」「操作してよい利用者か」「管理者か」を関数にまとめ、すべてのポリシーから同じ関数を使うと、抜け漏れが減ります
3. Storage(画像の保存場所)
- バケットを Public にしていないか:Public のバケットは、URL を知っていれば誰でも読めます。非公開にして、読み取りもポリシーで制限します
- 保存先のパスに利用者の ID を含める:
<user_id>/<ファイル名>のようにし、書き込みのポリシーで本人のフォルダにしか置けないようにします - ファイルの種類と大きさ:バケットの設定で、受け付ける MIME タイプと最大サイズを制限します
- 非表示・ブロックとの整合:投稿を非表示にしたり、利用者をブロックしたりしたときに、画像だけ読めてしまわないか
4. 認証とメール
- メールアドレスの確認を有効にしているか:確認前の利用者に投稿させない設計にします
- Site URL とリダイレクト先の許可リスト:公開したアドレスを登録します。登録し忘れると、メールのリンクが localhost を開きます
- メールのリンクの「先読み」:一部のメールサービスのセキュリティ機能は、届いたリンクを自動で開くことがあります。リンクを開いただけで確認が終わる作りだと、本人が押す前に使われてしまいます。リンク先にボタンを置き、押したときに確認する作りにすると防げます
- 送信元のドメイン:独自のメール送信サービス(SMTP)を設定し、SPF・DKIM・DMARC を整えます
5. 管理者権限とモデレーション
- 管理者の判定をデータベースで:管理画面を隠すだけでなく、非表示・利用停止などの操作そのものを、管理者だけが実行できるポリシーや関数にします
- 管理者を増やす手順:利用者が自分で管理者になれる経路(プロフィールの更新など)がないか
- 通報の読み取り:通報の内容は、通報した本人と管理者だけが読めるようにします
- 連続投稿の上限:1時間あたりの投稿数などの上限を、データベース側でも確かめます
6. 退会とデータの削除
- 関連する行が消えるか:プロフィールを起点に、投稿・返信・ブロック・通報などが外部キーの
on delete cascadeで消えるか - Storage のファイル:データベースの行が消えても、ファイルは自動では消えません。退会の処理で、その利用者のフォルダを消します
- Auth の利用者:認証の利用者そのものも削除されるか(サーバー側の処理で行う)
7. 鍵と、AI に触らせる範囲
- service_role(secret)鍵をブラウザに出さない:
NEXT_PUBLIC_で始まる環境変数に入れない。サーバー側だけで使います - .env.local をコミットしない・AI に見せない:AI のチャットや記録に、鍵が残らないようにします
- AI が変えてはいけない場所を決める:認証・RLS・マイグレーション・Storage のポリシーなどを「守る場所」として文書にし、変更されたら検出する仕組み(Git の hook や、CI での差分の確認)を用意します
8. テストで確かめる
ポリシーは、読んで正しそうに見えても、実際に試すと穴が見つかることがあります。次のような「失敗するはずの操作」を、テストとして残しておくのがおすすめです。
- 利用者 A として、利用者 B の投稿を更新・削除しようとすると失敗する
- B が A をブロックした後、A から B の投稿が見えない・返信できない
- 利用停止中の利用者は、投稿・返信・通報を作れない
- 管理者でない利用者は、投稿を非表示にできない・自分を管理者にできない
- ログインしていない状態では、どのテーブルも読めない(公開する設計なら、その範囲だけ)
- 退会すると、関連する行とファイルが残らない
テストは、トランザクションの中で利用者を切り替えて実行し、最後に取り消す(rollback)と、データベースを汚さずに何度でも試せます。
このチェックリストの多くを、最初から満たした土台
SNS Starter Kit は、RLS・Storage のポリシー・ブロック・通報・管理者権限・退会を実装し、データベースの安全の仕組みを実際に試すテスト(verify:full)を同梱しています。
まとめ
- Supabase では、画面ではなくデータベースの RLS とポリシーが最後の砦
- 関数・ビュー・Storage は、RLS とは別に確かめる
- 管理者・ブロック・利用停止の判定は、関数にまとめてすべてのポリシーで使う
- 「他人として操作したら失敗する」ことを、テストで残しておく
通報・ブロック・管理画面の設計については、コミュニティSNSの通報・ブロック・管理画面の作り方で詳しく説明しています。