● CWE-862 · OWASP A01:2021

Правила доступа в Supabase и Firebase (CWE-862): как отсутствие Row Level Security раскрывает данные и как это исправить

Приложения на Supabase и Firebase позволяют браузеру обращаться к базе данных напрямую с публичным ключом, поэтому между посетителем и каждой строкой стоят только Row Level Security или Security Rules. Таблицу, созданную через SQL без enable row level security, или правило Firestore, оставленное в виде allow read, write: if true, может читать и менять кто угодно. Как политики USING и WITH CHECK ограничивают строки вошедшим пользователем, на примере миграции PostgreSQL.

Простыми словами (ELI5)

Офисное здание выдаёт каждому посетителю одну и ту же карту для входа в холл, напечатанную на обороте буклета. Это не страшно, пока дверь каждого этажа проверяет, кто вы, прежде чем открыться. Один этаж оборудовали впопыхах, и на его двери вообще нет считывателя, поэтому карта холла её открывает, и любой может войти и читать документы. Ключ anon в Supabase — это карта холла: он задуман публичным. Row Level Security — это считыватель на двери каждого этажа. Исправление — поставить считыватель на каждую дверь и точно указать ему, чьи документы может видеть каждый человек.

Ключевые понятия и термины

Публичный ключ anon
Ключ anon Supabase и веб-конфигурация Firebase поставляются внутри пакета приложения, и любой может их скопировать. Они определяют проект, а не пользователя, поэтому не являются секретом и сами по себе данные не защищают.
Row Level Security (RLS)
Функция PostgreSQL, которая пропускает каждый запрос через политики. Table Editor в Supabase включает её для новых таблиц, но таблицы, созданные через SQL или миграции, начинают с выключенной RLS, и права по умолчанию позволяют anon и authenticated читать и менять любую строку.
USING и WITH CHECK
USING определяет, какие существующие строки пользователь может видеть, изменять или удалять; WITH CHECK — какие новые или изменённые строки он может записать. Политика только с USING для insert или update позволяет записывать строки для чужой организации.
Ключ service_role
Ключ service_role полностью обходит RLS. Ему место только на серверах и в edge-функциях, но никогда не в браузере или мобильном приложении.
Правила Firestore в тестовом режиме
Проекты, запущенные в тестовом режиме, получают правила, разрешающие любое чтение и запись до определённой даты, и многие затем превращают их в allow read, write: if true. Правила должны сверять request.auth.uid с полем владельца документа.

Пошаговый сценарий атаки

Шаг 1

Атакующий открывает приложение

URL Supabase и ключ anon лежат в JavaScript-бандле или пакете мобильного приложения.

Шаг 2

Он обращается к таблице напрямую

Он вызывает REST-эндпоинт invoices только с ключом anon, минуя экраны приложения. Запрос к тестовому проекту с ключом anon и без пользовательской сессии показывает этот случай.

Шаг 3

База возвращает все строки

RLS выключена, а у anon есть право select, поэтому PostgreSQL ничего не фильтрует и отдаёт счета всех организаций.

Шаг 4

Данные утекают или меняются

Если выданы ещё и update и delete, атакующий может изменять или стирать записи. В 2025 году был присвоен CVE-2025-48757 после того, как исследователи обнаружили, что отсутствие RLS раскрывало данные более чем в 170 приложениях, созданных одним ИИ-конструктором приложений.

Исходный код: Уязвимый 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())));

Чек-лист по защите системы для инженеров

Источники

← Весь каталог по безопасности Все руководства по уязвимостям →