FixVibe

// docs / baas security / supabase rls scanner

Supabase RLS スキャナ: 行レベルセキュリティが欠落または破損したテーブルを検出

Supabase をバックエンドにしたアプリをリリースしたとき、顧客のデータとインターネットの間に立ちはだかる唯一のものが行レベルセキュリティ (RLS) です。AI コーディングツールは、コンパイルが通り、デプロイされ、しかし静かにデータを漏洩させる RLS 風コードを生成します — RLS を有効化せずに作成されたテーブル、読み取りはできるが制限しないポリシー、列自体を比較する述語などです。本記事では、Supabase RLS スキャナが外部から何を証明できるか、バイブコードされたアプリに現れる 4 つの破損 RLS パターン、そして自分のデプロイ環境を 1 分以内にスキャンする方法を示します。

外部 RLS スキャンが証明できること

A passive RLS scan looks at your Supabase project the way an anonymous visitor would. It uses only the publishable anon key — the same key your browser uses — and never authenticates as a user or touches service-role privileges. Anything it can see, an unauthenticated attacker on the internet can see.

データベース外部から、スキャナは以下を高い信頼度で確認できます:

  • テーブルで RLS が無効化されている。 RLS がオフのとき、またはポリシーが許可しているとき、PostgREST は匿名 SELECT に対して行を返します。どちらの場合も検出対象です。
  • 匿名ロールがテーブルを列挙できる。 anon キーを使った GET /rest/v1/ は、anon ロールが何らかの権限を持つすべてのテーブルの OpenAPI スキーマを返します。AI 生成アプリはスキーマに対して USAGE を、各テーブルに対して SELECT を付与することが多く、RLS が実際の読み取りを拒否していてもスキーマ全体のマップが露出します。
  • Read access does not establish write safety. A passive scan cannot prove that INSERT, UPDATE, or DELETE policies protect every user and tenant. Review write policies and test them with isolated fixtures under the intended roles.
  • サービスロールキーがブラウザバンドルに含まれている。 RLS に隣接する問題: スキャナが JavaScript バンドル内で SUPABASE_SERVICE_ROLE_KEY または role: service_role を含む JWT を発見した場合、RLS は意味を失います — そのキーの保有者はあらゆるポリシーをバイパスします。

外部スキャンが証明できないこと

スキャナの境界について正直であるべきです。外部 RLS スキャンは、あなたの pg_policies テーブル、マイグレーションファイル、または特定ポリシーの厳密な述語を読むことはできません。ブラックボックスの挙動から推測するため、意図的に公開しているデータ (マーケティングニュースレターのテーブル、公開製品カタログなど) を 検出 として報告することがあります。FixVibe レポートは、スキャナが意図を判別できない場合は 中程度の信頼度 としてフラグを立てます — テーブル名を確認して判断してください。

AI ツールが生成する 4 つの破損 RLS パターン

When you point Cursor, Claude Code, Lovable, or Bolt at Supabase, the same four broken-RLS patterns come up again and again. Each one passes type-check, compiles, and ships:

パターン 1: RLS が一度も有効化されていない

The most common failure mode. The migration creates the table but the developer (or the AI tool) forgets ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST happily serves the entire table to anyone with the anon key. Fix: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;, then add a policy per command. Enabling RLS with no policies denies all access through the API; the policies decide who gets in.

sql
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;

パターン 2: RLS は有効、ポリシーなし

より微妙な失敗。RLS は有効ですが、ポリシーが書かれていません。PostgreSQL のデフォルトは 拒否 なので、認証済みユーザーには何も見えません — そこで開発者は USING (true) を追加してアプリを動かそうとし、結果として誰もがすべてを読めるようになります。修正: auth.uid() でスコープを絞ったポリシーを書きます: CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id);、加えて対応する INSERT/UPDATE/DELETE ポリシー。

sql
CREATE POLICY "select_own"
  ON public.[name]
  FOR SELECT
  USING (auth.uid() = user_id);

パターン 3: ポリシーが列を自分自身と比較する

コピー&ペーストの遺物。開発者は USING (auth.uid() = user_id) ではなく USING (user_id = user_id) と書きます — これは常に真です。型チェックは通り、ポリシーはすべての行を許可します。修正: 列は常に関数呼び出し (auth.uid()、auth.jwt()->>'org_id' など) と比較し、自分自身や定数とは決して比較しないでください。

Shape 4: Overly permissive write policies

With RLS enabled, a role with no applicable policy is denied by default. A SELECT policy does not itself allow INSERT. Write exposure can arise from a permissive applicable INSERT or ALL policy, disabled RLS, or a role that bypasses RLS. Fix: inspect table privileges and every applicable policy, use ownership-scoped WITH CHECK conditions, and test writes with separate user identities.

FixVibe Supabase RLS スキャナの仕組み

FixVibe's Supabase check works from the deployed app, in three steps:

  1. Find the project. FixVibe loads the deployed app and reads the Supabase project URL and public key from the same configuration your browser receives. No guessing, no brute force.
  2. See what is exposed. FixVibe checks which tables the anonymous role can see, without reading row data at this step.
  3. Stage 3 — inspect anonymous read access. The passive check reports observable access to discovered resources. It does not create, update, or delete database rows, and it cannot establish that all write policies or cross-user boundaries are correct.

Each finding ships with the exact request URL, response status, response shape (header-only), and the table name. FixVibe now labels whether the fix belongs in code/config, a provider console, DNS, secret rotation, or manual review so the next step matches who can actually apply it.

スキャナが何かを検出したときの対応

すべての RLS 検出はランタイムの緊急事態です。公開 PostgREST エンドポイントは数分以内に攻撃者にスキャンされます。修復シーケンスは機械的です:

  1. すべてのテーブルを監査。 Supabase SQL エディタで SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; を実行します。rowsecurity = false の行はすべて問題です。
  2. Enable RLS on every public table. Default to ENABLE ROW LEVEL SECURITY on every table created — make it a migration template.
  3. コマンドごとにポリシーを作成。 FOR ALL USING (true) を使ってはいけません。SELECT、INSERT、UPDATE、DELETE それぞれに明示的なポリシーを書き — auth.uid() や auth.jwt() から取得した組織 ID 列でスコープを絞ります。
  4. 別アカウントで検証。 別のユーザーとしてサインアップし、REST API 経由で他のユーザーのレコードを直接読み取ろうとします。レスポンスが 200 なら、ポリシーが壊れています。
  5. Re-scan. After applying the fix, re-run a FixVibe scan against the same URL. The Supabase RLS finding should clear.
sql
-- Audit every table for missing RLS. Run in the Supabase SQL editor.
SELECT schemaname, tablename, rowsecurity
FROM   pg_tables
WHERE  schemaname = 'public'
ORDER  BY rowsecurity, tablename;

他のスキャナとの比較

ほとんどの汎用 DAST ツール (Burp Suite、OWASP ZAP、Nessus) は PostgREST を理解しません。アプリをクロールし、/rest/v1/ パスを無視し、理解できる HTML ページについて報告するだけです。Snyk と Semgrep は静的解析ツールです — リポジトリ内の RLS 呼び出しが欠落したマイグレーションファイルを発見しますが、デプロイされたデータベースが設定ミスを起こしていることを証明できません。FixVibe はその隙間に位置します: パッシブ、BaaS を理解し、公開 URL から未認証攻撃者が証明できることに焦点を当てています。

よくある質問

スキャナは私のデータを読んだり変更したりしますか?

The passive RLS check is read-only. It examines observable anonymous read access and reports limited evidence. It does not perform database write operations or prove that every user and tenant is correctly isolated.

Supabase プロジェクトが一時停止中だったり、カスタムドメインを使用している場合でも動作しますか?

一時停止中のプロジェクトはすべてのリクエストに 503 を返します — スキャナはプロジェクトを到達不能と報告します。デプロイされたアプリがブラウザで依然として Supabase クライアント SDK を読み込んでいる限り、カスタムドメインでも動作します。スキャナはどちらの場合もバンドルからプロジェクト URL を抽出します。

anon キーがローテーションされた場合や、公開可能キーが変更された場合はどうなりますか?

スキャンを再実行してください。スキャナは毎回現在のバンドルからキーを再抽出します。ローテーションは以前のレポートのみを無効化し、データベースのポリシー状態には影響しません。

スキャナは新しい Supabase 公開可能キーモデル (sb_publishable_*) もチェックしますか?

はい。検出器はレガシーの anon JWT と新しい sb_publishable_* キーの両方を認識し、同じように扱います — どちらも公開を想定しており、どちらも RLS を唯一の防御線として残します。

次のステップ

Run a free FixVibe scan against your production URL — the Supabase RLS check runs on every plan including the free tier. For a deeper read on what else can leak from a Supabase project, see Supabase service role key exposed in JavaScript and Supabase storage bucket security checklist. For the umbrella view across all BaaS providers, read BaaS misconfiguration scanner.

// baas サーフェスをスキャン

他の誰かよりも先に、開いているテーブルを見つけてください。

本番 URL を入力してください。FixVibe はあなたのアプリが通信する BaaS プロバイダを列挙し、公開エンドポイントを識別し、未認証クライアントが読み書きできる内容を報告します。無料、インストール不要、カード登録不要。

  • 無料プラン — 月 3 回のスキャン、サインアップにカード不要。
  • パッシブな BaaS フィンガープリント — ドメイン所有確認不要。
  • Supabase、Firebase、Clerk、Auth0、Appwrite ほか。
  • Coding-agent prompts where code/config applies, plus provider-console steps for hosted BaaS fixes.
Supabase RLS スキャナ: 行レベルセキュリティが欠落または破損したテーブルを検出 · FixVibe