● CWE-862 · OWASP A01:2021

Supabase と Firebase のアクセスルール(CWE-862):Row Level Security の欠如がデータを露出させる仕組みと直し方

Supabase や Firebase のアプリでは、ブラウザが公開キーでデータベースを直接問い合わせるため、訪問者とすべての行の間にあるのは Row Level Security か Security Rules だけです。enable row level security なしで SQL から作ったテーブルや、allow read, write: if true のままの Firestore ルールは、誰でも読み書きできます。USING と WITH CHECK のポリシーで行をログイン中のユーザーに限定する方法を、PostgreSQL のマイグレーション例で解説します。

わかりやすい解説 (ELI5)

あるオフィスビルでは、来訪者全員にパンフレットの裏に印刷された同じロビー用カードを渡しています。各階の扉が開く前に本人確認をしている限り、それで問題はありません。ところが急いで準備した階の扉にはカードリーダーがなく、ロビーのカードで開いてしまい、誰でも入って書類を読めます。Supabase の anon キーはこのロビー用カードで、公開される前提のものです。Row Level Security は各階の扉のカードリーダーです。対策は、すべての扉にリーダーを付け、誰がどの書類を見てよいかを正確に教えることです。

主要な概念と専門用語

公開の anon キー
Supabase の anon キーと Firebase の Web 設定はアプリのバンドルに含まれ、誰でもコピーできます。これらはユーザーではなくプロジェクトを識別するもので、秘密ではなく、それだけではデータを守れません。
Row Level Security(RLS)
すべてのクエリをポリシーで絞り込む PostgreSQL の機能です。Supabase の Table Editor は新しいテーブルで有効にしますが、SQL やマイグレーションで作ったテーブルは RLS がオフの状態で始まり、既定の権限により anon と authenticated がすべての行を読み書きできてしまいます。
USING と WITH CHECK
USING は、ユーザーが参照・更新・削除できる既存の行を決め、WITH CHECK は、書き込める新しい行や変更後の行を決めます。insert や update のポリシーに USING しかないと、別の組織の行を書き込めてしまいます。
service_role キー
service_role キーは RLS を完全に迂回します。サーバーやエッジ関数だけで使い、ブラウザやモバイルアプリには決して入れません。
Firestore のテストモードのルール
テストモードで始めたプロジェクトには、ある日付まですべての読み書きを許すルールが入り、その後 allow read, write: if true に書き換えられることもよくあります。ルールでは request.auth.uid をドキュメントの所有者フィールドと照合する必要があります。

ステップ・バイ・ステップの攻撃フロー

ステップ 1

攻撃者がアプリを開く

Supabase の URL と anon キーは JavaScript のバンドルやモバイルアプリのパッケージに入っています。

ステップ 2

テーブルを直接問い合わせる

アプリの画面を通さず、anon キーだけで invoices の REST エンドポイントを呼び出します。anon キーだけを使いユーザーセッションなしでテスト用プロジェクトに送るリクエストは、この場合を示しています。

ステップ 3

データベースがすべての行を返す

RLS がオフで、anon に select 権限があるため、PostgreSQL は何も絞り込まず全組織の請求書を返します。

ステップ 4

データが漏れたり書き換えられたりする

update と delete も許可されていれば、攻撃者はレコードを変更・削除できます。2025 年、ある AI アプリビルダーが生成した 170 を超えるアプリで RLS の欠如によりデータが露出していることを研究者が見つけ、CVE-2025-48757 が割り当てられました。

ソースコード比較:脆弱 vs 堅牢化

脆弱な実装
-- migration.sql: このテーブルでは RLS が一度も有効にされない
create table public.invoices (
  id bigint generated always as identity primary key,
  org_id uuid not null references public.orgs (id),
  customer text not null,
  amount_cents bigint not null
);

-- 公開の anon キーですべての行を読み、変更し、削除できる
grant select, insert, update, delete on public.invoices to anon, authenticated;
堅牢化されたセキュアパッチ
-- migration.sql: RLS を有効化、anon の権限を取り消し、コマンドごとにポリシー
create table public.invoices (
  id bigint generated always as identity primary key,
  org_id uuid not null references public.orgs (id),
  customer text not null,
  amount_cents bigint not null
);

-- RLS が有効なら、ポリシーがない操作はすべて拒否される
alter table public.invoices enable row level security;
revoke all on public.invoices from anon;

-- ユーザーは自分が所属する組織の請求書だけを見られる
create policy "members read their org's invoices"
  on public.invoices for select to authenticated
  using (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));

-- 新しい行はユーザーの組織のものに限る。update と delete のポリシーはなし
create policy "members add invoices to their org"
  on public.invoices for insert to authenticated
  with check (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));

エンジニアリング&システム堅牢化チェックリスト

参考資料

← セキュリティ目録をすべて見る 脆弱性ガイド一覧 →