FixVibe

// docs / baas security / supabase rls scanner

Сканер Supabase RLS: найдите таблицы с отсутствующей или сломанной безопасностью на уровне строк

Безопасность на уровне строк (RLS) — это единственное, что стоит между данными ваших клиентов и интернетом, когда вы выпускаете приложение на основе Supabase. ИИ-инструменты кодирования генерируют код в форме RLS, который компилируется, выпускается и тихо сливает данные — таблицы, созданные без включения RLS, политики, которые читают, но никогда не ограничивают, предикаты, которые сравнивают столбец с самим собой. Эта статья показывает, что сканер Supabase RLS может доказать извне, четыре формы сломанного RLS, которые появляются в vibe-кодированных приложениях, и как сканировать ваше собственное развертывание менее чем за минуту.

Что может доказать внешнее сканирование 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 отключён на таблице. PostgREST возвращает строки для анонимного SELECT, когда RLS отключён или когда политика это разрешает. Любой случай — это находка.
  • Анонимная роль может перечислять таблицы. GET /rest/v1/ с ключом anon возвращает схему OpenAPI для каждой таблицы, к которой у роли anon есть любые привилегии. ИИ-сгенерированные приложения часто предоставляют 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: если сканер находит SUPABASE_SERVICE_ROLE_KEY или любой JWT с role: service_role в JavaScript-бандле, RLS становится бесполезным — владелец этого ключа обходит каждую политику.

Что внешнее сканирование не может доказать

Будьте честны относительно границ сканера. Внешнее сканирование RLS не может прочитать вашу таблицу pg_policies, ваши файлы миграции или точный предикат какой-либо политики. Оно делает выводы из поведения чёрного ящика, что означает, что иногда оно сообщит о находке, которая окажется намеренно публичными данными (таблица маркетинговой рассылки, публичный каталог продуктов). Отчёт FixVibe помечает их как средний уровень доверия, когда сканер не может различить намерения — пересмотрите имя таблицы и примите решение.

Четыре формы сломанного 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 (user_id = user_id) — что всегда истинно — вместо USING (auth.uid() = 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. Проверьте каждую таблицу. Запустите SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; в редакторе SQL Supabase. Любая строка с 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() или столбец org-id из auth.jwt().
  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 Client SDK в браузере; сканер извлекает URL проекта из бандла в любом случае.

Что, если мой ключ anon ротирован или мой публикуемый ключ изменён?

Повторно запустите сканирование. Сканер повторно извлекает ключ из текущего бандла при каждом запуске. Ротация делает недействительным только предыдущий отчёт, а не состояние политики базы данных.

Проверяет ли сканер новую модель публикуемых ключей Supabase (sb_publishable_*)?

Да. Детектор распознаёт как устаревшие JWT anon, так и новые ключи 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