Supabase RLS の設計パターン — デフォルト拒否で安全に

2026.02.01
Share:

Supabase の Row Level Security(RLS)は、データベース層でアクセス制御を行う強力な機能です。しかし、設計を間違えるとセキュリティホールになったり、開発効率が落ちたりします。

この記事では、2つのプロダクト(Wakulier と BandBridge)を開発する中で学んだ RLS の設計パターンを紹介します。

なぜ RLS を使うのか

2025年12月27日、Wakulier の開発を始めたときの記録です:

Supabase: PostgreSQL + Auth + Storage + Realtime を一括提供。個人開発に最適 RLS優先: セキュリティをデータベース層で担保。APIルートでの漏れを防ぐ

API ルートで毎回「このユーザーはこのデータにアクセスできるか」をチェックするのは面倒ですし、漏れのリスクがあります。RLS を使えば、データベース層で一貫したアクセス制御ができます。

原則:デフォルト拒否

BandBridge 開発時(2026年1月7日)に学んだ最も重要な原則です:

RLSは「デフォルト拒否」で設計し、必要な権限だけ付与するのが安全

具体的には、こういうことです:

-- まず全てのアクセスを拒否
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;

-- 必要な権限だけ明示的に付与
CREATE POLICY "Users can view their own profile"
  ON profiles FOR SELECT
  USING (auth.uid() = user_id);

CREATE POLICY "Users can update their own profile"
  ON profiles FOR UPDATE
  USING (auth.uid() = user_id);

「とりあえず全部許可して、後から制限する」というアプローチは危険です。RLS を有効にした時点でデフォルトは「全拒否」なので、必要な権限だけを追加していきます。

RLS 設計で悩んだこと

BandBridge では、複雑なデータモデル(プロフィール、バンド、募集、スカウト、メッセージ、ブロック)を扱いました。

2026年1月7日の開発日記から:

RLSポリシー設計: 「誰が何を見れるか」のルール設計が複雑。最初はシンプルに、後から拡張する方針に

結局、以下の方針で設計しました:

  1. 最初はシンプルに:自分のデータだけ見える/編集できるポリシーから始める
  2. ユースケースごとに追加:「他人のプロフィールを見たい」→ 公開プロフィール用ポリシーを追加
  3. 例外はドキュメント化:なぜこのポリシーが必要なのかをコメントで残す

ブロック機能と RLS

BandBridge ではブロック機能を実装しました。ブロックされたユーザーはスカウトやメッセージを送れなくなります。

ブロックのRLS制限: APIレベルで完全にブロック。クライアントサイドのみの制限は突破される可能性がある

クライアント側で「ブロックされていたら送信ボタンを無効化」だけでは不十分です。API を直接叩かれる可能性があるからです。RLS でデータベース層からブロックすることで、完全な制御ができます:

CREATE POLICY "Cannot send scout to blocked user"
  ON scouts FOR INSERT
  WITH CHECK (
    NOT EXISTS (
      SELECT 1 FROM blocks
      WHERE blocker_id = target_user_id
        AND blocked_id = auth.uid()
    )
  );

初期ユーザー問題

Wakulier 開発時に「鶏と卵」問題にぶつかりました:

「最初のクライアントを誰が作成するか」問題。 RLSで「ownerのみ作成可能」にしたが、最初のownerがいない

解決策は、環境変数で「管理者メールアドレス」を定義し、そのユーザーに初期作成権限を付与することでした:

CREATE POLICY "Admin can create first workspace"
  ON workspaces FOR INSERT
  WITH CHECK (
    auth.jwt() ->> 'email' = current_setting('app.admin_email', true)
    OR
    EXISTS (SELECT 1 FROM workspaces WHERE owner_id = auth.uid())
  );

RLS first 設計のメリット

Wakulier 開発で気づいたことです:

「RLS first」で設計すると、APIルートの実装がシンプルになる

API ルートでは認証チェック(「ログインしているか」)だけ行い、認可チェック(「このデータにアクセスできるか」)は RLS に任せます。これにより:

  1. API ルートのコードがシンプルになる
  2. 認可ロジックが一箇所(RLS ポリシー)に集約される
  3. 新しい API を追加しても、既存のポリシーが適用される

まとめ

2つのプロダクト開発で学んだ RLS 設計のポイント:

  1. デフォルト拒否:RLS を有効にして、必要な権限だけ追加
  2. シンプルから始める:自分のデータだけのポリシーから、徐々に拡張
  3. API ではなく DB で制御:クライアントサイドの制限は突破される
  4. 初期ユーザー問題:環境変数で管理者を定義
  5. RLS first:認可は DB に任せて、API はシンプルに

RLS は最初は複雑に感じますが、一度パターンを掴めば強力な武器になります。個人開発でバックエンドの工数を最小化しつつ、セキュアなアプリを作りたい方は、ぜひ試してみてください。

Zeronova avatar

Zeronovaゼロノバ

Product Manager / AI-Native Builder

Web/IT業界19年以上・20以上のWebサービスを担当したPdM。東証プライム上場企業の子会社代表として事業経営を経験。現在はAIを駆使して企画から実装まで完結させる個人開発を実践中。

関連プロダクト

Wakulier(ワクリア)

継続案件の依頼管理ツール

BandBridge(バンドブリッジ)

ミュージシャンとバンドをつなぐ