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ポリシー設計: 「誰が何を見れるか」のルール設計が複雑。最初はシンプルに、後から拡張する方針に
結局、以下の方針で設計しました:
- 最初はシンプルに:自分のデータだけ見える/編集できるポリシーから始める
- ユースケースごとに追加:「他人のプロフィールを見たい」→ 公開プロフィール用ポリシーを追加
- 例外はドキュメント化:なぜこのポリシーが必要なのかをコメントで残す
ブロック機能と 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 に任せます。これにより:
- API ルートのコードがシンプルになる
- 認可ロジックが一箇所(RLS ポリシー)に集約される
- 新しい API を追加しても、既存のポリシーが適用される
まとめ
2つのプロダクト開発で学んだ RLS 設計のポイント:
- デフォルト拒否:RLS を有効にして、必要な権限だけ追加
- シンプルから始める:自分のデータだけのポリシーから、徐々に拡張
- API ではなく DB で制御:クライアントサイドの制限は突破される
- 初期ユーザー問題:環境変数で管理者を定義
- RLS first:認可は DB に任せて、API はシンプルに
RLS は最初は複雑に感じますが、一度パターンを掴めば強力な武器になります。個人開発でバックエンドの工数を最小化しつつ、セキュアなアプリを作りたい方は、ぜひ試してみてください。
Zeronova(ゼロノバ)
Product Manager / AI-Native Builder
Web/IT業界19年以上・20以上のWebサービスを担当したPdM。東証プライム上場企業の子会社代表として事業経営を経験。現在はAIを駆使して企画から実装まで完結させる個人開発を実践中。