Supabaseでは認証を導入しただけでは十分ではありません。
ユーザーごとのデータアクセスを安全に制御するためには、 RLS(Row Level Security) の設定が必要です。
しかし、
- RLSを有効化したらデータが取得できなくなった
- auth.uid() がよくわからない
- Policyの書き方がわからない
という人も多いのではないでしょうか。
この記事では、SupabaseでRLSを設定する方法を実際のSQL付きでわかりやすく解説します。
RLS設定が必要な理由#
認証だけでは、
ログイン済みユーザー
↓
postsテーブルへアクセス
↓
他人のデータも取得できる可能性があります。
そこで利用するのがRLSです。
RLSとは#
RLS(Row Level Security)とは、
行(レコード)単位でアクセス制御を行うPostgreSQLの機能
です。
例えば以下のようなデータがあるとします。
| user_id | title |
|---|---|
| userA | 投稿A |
| userB | 投稿B |
RLSを設定すると
- userA → 投稿Aのみ取得可能
- userB → 投稿Bのみ取得可能
になります。
RLSを有効化する#
まず対象テーブルでRLSを有効にします。
ALTER TABLE posts
ENABLE ROW LEVEL SECURITY;または
Table Editor
↓
対象テーブル
↓
Enable RLSをONにします。
RLSを有効化するとどうなる?#
実はRLSをONにしただけでは
誰もアクセスできない状態になります。
つまり
SELECT * FROM posts;を実行しても取得できません。
Policyとは#
RLSでは
誰に何を許可するか
をPolicyで定義します。
SELECTを許可する#
自分の投稿だけ取得可能にする例です。
CREATE POLICY "Users can view own posts"
ON posts
FOR SELECT
USING (
auth.uid() = user_id
);動作イメージ#
ログインユーザー
↓
auth.uid()
↓
user_idと比較
↓
一致した行だけ取得auth.uid()とは#
RLSで最も重要なのが
auth.uid()です。
これは
現在ログインしているユーザーID
を取得する関数です。
JWTとの関係#
内部では以下のように動いています。
ログイン
↓
JWT発行
↓
リクエスト送信
↓
auth.uid()
↓
Policy評価つまり
auth.uid()はJWTから取得されています。
INSERTを許可する#
投稿作成を許可する場合。
CREATE POLICY "Users can insert own posts"
ON posts
FOR INSERT
WITH CHECK (
auth.uid() = user_id
);UPDATEを許可する#
CREATE POLICY "Users can update own posts"
ON posts
FOR UPDATE
USING (
auth.uid() = user_id
);DELETEを許可する#
CREATE POLICY "Users can delete own posts"
ON posts
FOR DELETE
USING (
auth.uid() = user_id
);USINGとWITH CHECKの違い#
初心者が混乱しやすいポイントです。
USING#
既存データへのアクセス制御
FOR SELECT
FOR UPDATE
FOR DELETEで利用
WITH CHECK#
新規データの検証
FOR INSERTで利用
未ログインユーザーの扱い#
未認証状態では
auth.uid()は
NULLになります。
つまり
auth.uid() = user_idは常に偽になります。
結果として
- 取得不可
- 更新不可
- 削除不可
になります。
全員に公開したい場合#
ブログ記事などを全員が閲覧できるようにする場合。
CREATE POLICY "Public Posts"
ON posts
FOR SELECT
USING (true);service_roleに注意(重要)#
Supabase初心者が最もハマりやすいポイントです。
service_roleはRLSを無視する#
サーバー側で利用する
SUPABASE_SERVICE_ROLE_KEYはRLSをバイパスできます。
つまり
createClient(url, serviceRoleKey)で実行すると
auth.uid() = user_idは評価されません。
安全な運用#
基本的には
ブラウザ
↓
anon key
サーバー
↓
service_roleで使い分けます。
RLSのパフォーマンス#
RLSは内部的に
WHERE auth.uid() = user_idのような条件として評価されます。
そのため
user_idにはインデックスを付与しておくのがおすすめです。
推奨#
CREATE INDEX idx_posts_user_id
ON posts(user_id);大量データでは性能差が大きくなります。
よくあるミス#
RLSをONにしただけ#
RLS ON
↓
Policyなし
↓
何も取得できないauth.uid()がNULL#
原因
- 未ログイン
- JWT未送信
USINGとWITH CHECKを間違える#
FOR INSERTでは
WITH CHECKを使います。
service_roleでテストしてしまう#
RLSが効いていると勘違いしやすいので注意です。
実務でのベストプラクティス#
① RLSは必ずON#
Supabaseでは基本必須です。
② auth.uid()を基本にする#
auth.uid() = user_idが最も一般的です。
③ service_roleはサーバー限定#
クライアントへ公開しない。
④ user_idにインデックスを貼る#
大量データで効果的です。
⑤ 本番前にポリシー確認#
SELECT *
FROM pg_policies;で確認できます。
まとめ#
SupabaseでRLSを設定する流れは以下の通りです。
RLS有効化
↓
Policy作成
↓
auth.uid()でユーザー判定
↓
アクセス制御重要ポイント
- RLSはDBレベルのアクセス制御
- auth.uid()でユーザーを識別
- JWTと連携して動作する
- service_roleはRLSをバイパスする
- user_idにはインデックスを付与する
👉 結論:Supabaseでは認証だけでなくRLSまで設定して初めて安全なアプリになる











