開発・個人開発

Supabaseの行レベルセキュリティ(RLS)入門 設定しないと起きる問題と実装方法

Supabaseの行レベルセキュリティ(RLS)入門 設定しないと起きる問題と実装方法

この記事の要点

SupabaseのRLS(Row Level Security)を設定していないと、anon keyを使えば誰でもデータベースの全データを読み書きできる。RLSの仕組み・設定方法・よく使うポリシーパターンを実装コードで解説する。

結論

Supabaseを使う場合、RLS(Row Level Security)の設定は必須だ。設定しないと、anon keyを使えば誰でもデータベースを操作できる状態になる。設定は「RLSを有効化する」「ポリシーを追加する」の2ステップで完了し、最初から正しく設定しておくことでセキュリティリスクを根本から防げる。

RLSを設定しないと何が起きるか

Supabaseのanonキーは、フロントエンドのコードに組み込んで使うものだ。ブラウザのデベロッパーツールを使えば誰でも確認できる。これは設計通りで、問題ではない。

問題になるのは、RLSが設定されていない状態だ。RLSがないテーブルは、anon keyさえあれば、どのテーブルのどのデータにも自由にアクセスできてしまう。

例えば、usersテーブルにRLSが設定されていない場合:

// 悪意あるユーザーが実行できてしまうコード
const { data } = await supabase.from('users').select('*');
// 全ユーザーのデータが返ってきてしまう

RLSが設定されていれば、ポリシーで許可されていないアクセスはブロックされる。

RLSの仕組み

RLSはPostgreSQLの機能で、テーブルへのアクセスを行単位で制御する。

RLSを有効化すると、デフォルトではすべてのアクセスがブロックされる。その上でポリシーを追加し、「誰が」「どの行に」「どんな操作を」できるかを定義する。

-- RLSを有効化
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- ポリシーを追加
CREATE POLICY "ポリシー名" ON my_table
  FOR SELECT  -- 操作の種類(SELECT/INSERT/UPDATE/DELETE/ALL)
  USING (条件);  -- アクセスを許可する条件

Supabaseではauth.uid()(現在の認証ユーザーのID)とauth.role()anonまたはauthenticated)がポリシー内で使える便利な関数だ。

よく使うポリシーパターン

パターン1:全員が読み取り可能・認証ユーザーのみ書き込み

記事一覧・カウンター・プロフィールなど、「誰でも読めるが書き込みは制限したい」ケースに使う。

-- RLSを有効化
ALTER TABLE articles ENABLE ROW LEVEL SECURITY;

-- 全員が読み取り可能
CREATE POLICY "Public read" ON articles
  FOR SELECT USING (true);

-- 認証済みユーザーのみ挿入可能
CREATE POLICY "Authenticated insert" ON articles
  FOR INSERT WITH CHECK (auth.role() = 'authenticated');

-- 認証済みユーザーのみ更新可能
CREATE POLICY "Authenticated update" ON articles
  FOR UPDATE USING (auth.role() = 'authenticated');

パターン2:自分のデータのみアクセス可能

ユーザーが自分のデータだけを読み書きできる設計。各行にuser_idカラムを持たせる。

-- テーブルのuser_idと認証ユーザーのIDが一致する行だけアクセス可能
ALTER TABLE user_notes ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users own data" ON user_notes
  FOR ALL USING (auth.uid() = user_id);

この設定では、ログインしていないユーザーは何も取得できない(auth.uid()がnullになり、条件が常にfalseになるため)。

パターン3:公開フラグがついた行だけ全員が読める

記事の公開/下書き管理などに使う。

ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- is_publicがtrueの行だけ全員が読める
CREATE POLICY "Public posts visible" ON posts
  FOR SELECT USING (is_public = true);

-- 自分の投稿は全て読める(下書き含む)
CREATE POLICY "Author reads own" ON posts
  FOR SELECT USING (auth.uid() = author_id);

-- 自分の投稿だけ更新可能
CREATE POLICY "Author updates own" ON posts
  FOR UPDATE USING (auth.uid() = author_id);

パターン4:カウンターの更新(認証なしで許可)

アクセスカウンターなど、誰でも更新できてよいケースに使う。

ALTER TABLE page_views ENABLE ROW LEVEL SECURITY;

-- 全員が読み取り可能
CREATE POLICY "Public read" ON page_views
  FOR SELECT USING (true);

-- 全員が挿入可能
CREATE POLICY "Public insert" ON page_views
  FOR INSERT WITH CHECK (true);

-- 全員が更新可能(カウンターのインクリメント用)
CREATE POLICY "Public update" ON page_views
  FOR UPDATE USING (true);

RLSの設定確認方法

Supabaseダッシュボードでの確認

  1. ダッシュボード → Table Editor → 対象のテーブルを選択
  2. テーブル名の横に「RLS enabled」のバッジが表示されていればRLSが有効
  3. Authentication → Policiesメニューで全テーブルのポリシー一覧を確認できる

SQLでの確認

-- RLSが有効なテーブルの一覧
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public';
-- rowsecurity が true であればRLS有効

anon keyとservice_role keyの使い分け

キー用途RLSの影響
anon keyフロントエンド(ブラウザ・クライアントサイド)RLSに従う
service_role keyサーバーサイド・Edge Functions・管理者操作RLSをバイパスする

service_role keyはRLSをバイパスするため、バックエンドの管理者操作に使うことができる。ただし、このキーは絶対にフロントエンドのコードに含めてはいけない。漏洩するとRLSが無効化された状態と同じになる。

よくある問題とデバッグ

RLSポリシーが正しいのにデータが取得できない

ポリシーのUSING句の条件を確認する。auth.uid()がnullになっていないか(認証されていない状態でアクセスしていないか)、カラム名の誤字がないかをチェックする。

-- ポリシーをデバッグするためのクエリ例
-- 現在のユーザーIDを確認
SELECT auth.uid();
-- 'authenticated'か'anon'を確認
SELECT auth.role();

全てのアクセスがブロックされる

RLSを有効化したが、ポリシーを追加していない状態。RLS有効化後は必ずポリシーを追加する必要がある。

Supabaseの基本的なセットアップについてはSupabaseの始め方を、アクセスカウンターの実装はSupabaseでアクセス数カウンターを作るで解説している。

まとめ

Supabaseを使う場合、テーブル作成と同時にRLSを有効化することを習慣にする。「有効化してポリシーなし」はすべてブロック、「有効化してポリシーあり」は条件に従ったアクセス制御、「無効」は誰でもアクセス可能という3つの状態を理解しておく。anon keyはフロントエンドにしか使わない・service_role keyはバックエンド専用という使い分けと合わせて設計することで、セキュリティの基盤が整う。

よくある質問

RLSを設定しないとどんな問題が起きますか

SupabaseのPublicable anon keyはフロントエンドのコードに含まれるため、誰でも確認できます。RLSがないテーブルは、このキーさえあれば誰でも全データを読み取り・書き込み・削除できる状態になります。個人情報や機密データがあれば深刻なリスクです。

RLSはすべてのテーブルに設定が必要ですか

フロントエンドから直接アクセスするテーブルは必ずRLSを設定してください。サーバーサイド(Edge Functions)からservice_role keyでのみアクセスするテーブルは、RLSなしでもservice_role keyがなければアクセスできないため安全です。ただし、原則として全テーブルにRLSを有効化することを推奨します。

RLSを設定すると速度は低下しますか

多少のオーバーヘッドはありますが、個人開発の規模では体感できる差はありません。ポリシーに使うカラムにインデックスを設定することで、パフォーマンスへの影響を最小化できます。

認証なしでデータを読み取れるようにするにはどうすればいいですか

「全員に読み取りを許可するポリシー」を作成することで、認証なしでもSELECTが可能になります。ただしINSERT・UPDATE・DELETEは認証済みユーザーに限定する設計が一般的です。