Курс

Настраиваем RLS-политики

МОДУЛЬ 6: БЕЗОПАСНОСТЬ • Урок 2 из 2

Теперь настроим защиту на уровне базы данных. Пройдём все шаги по порядку.

[ ШАГ 1: ДОБАВЛЯЕМ КОЛОНКУ ВЛАДЕЛЬЦА ]

Чтобы база данных знала кто владелец записи, нужна колонка user_id.

Сейчас в таблице tasks просто данные. Кто их владелец — неизвестно.

Добавим колонку user_id — это будет ID владельца записи.

Скажи Claude:

"Добавь в таблицу tasks колонку user_id. Она должна ссылаться на auth.users"

Claude напишет SQL примерно такой:

alter table tasks
add column user_id uuid references auth.users(id);

Зайди в SQL Editor в Supabase и выполни этот запрос.

Теперь у каждой задачи (напоминаю: мы делаем веб-приложение для заметок, в качестве примера) может быть владелец (старые записи останутся пустые, но новые будут с владельцем).

[ ШАГ 2: УСКОРЯЕМ ПОИСК ]

Сейчас нужно добавить индекс на колонку user_id.

Что такое индекс?

Индекс это алфавитный указатель для базы данных. Он помогает быстро найти нужные строки.

Зачем это нужно:

Без индекса на user_id база данных будет проверять КАЖДУЮ строку в таблице: "это запись Васи? а это? а это?". На 10 000+ записей это может занять секунды. С индексом — мгновенно.

Скажи Claude:

"Создай индекс на колонку user_id в таблице tasks"

Или выполни сам в SQL Editor:

create index tasks_user_id_idx on tasks(user_id);

Теперь поиск по владельцу будет быстрым.

[ ШАГ 3: ВКЛЮЧАЕМ RLS ]

Сейчас таблица открыта. Включим защиту.

alter table tasks enable row level security;

Важно: После выполнения этой команды все данные исчезнут из приложения.

Почему? Потому что RLS включён, но правил доступа ещё нет. По умолчанию правило одно: "Никому ничего". Даже тебе. Даже администратору. (Кроме service_role, но это особый ключ, он не для игр).

⚠️ Данные не удалились:

Они просто спрятались. Сейчас создадим правила доступа и данные вернутся.

[ ШАГ 4: СОЗДАЁМ ПРАВИЛА ДОСТУПА ]

Нужно объяснить базе данных кто что может делать.

Два типа проверки:

  1. USING — фильтр. Показывает только те записи, которые проходят проверку.
  2. WITH CHECK — проверка при изменении. Позволяет создать/изменить запись только если она проходит проверку.

Важно:

  • INSERT политики должны иметь только WITH CHECK (не USING)
  • UPDATE политики должны иметь И USING И WITH CHECK

Без WITH CHECK злоумышленник может создать запись с чужим user_id.

Тоже важно: роли

В Supabase есть две роли:

  • authenticated — пользователь вошёл в систему (залогинен)
  • anon — пользователь НЕ вошёл (анонимный гость)

Мы разрешим действия только для authenticated — только для тех, кто вошёл.

Скажи Claude:

"Напиши RLS политики для tasks. Только для authenticated роли. Пользователь должен видеть и менять только свои записи. Используй auth.uid() для проверки владельца."

Код будет примерно такой:

-- ЧИТАТЬ только свои записи
create policy "Users can view own tasks"
  on tasks for select
  to authenticated
  using (auth.uid() = user_id);

-- СОЗДАВАТЬ только свои записи (проверяем что user_id = текущий пользователь)
create policy "Users can insert own tasks"
  on tasks for insert
  to authenticated
  with check (auth.uid() = user_id);

-- ОБНОВЛЯТЬ только свои записи
create policy "Users can update own tasks"
  on tasks for update
  to authenticated
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

-- УДАЛЯТЬ только свои записи
create policy "Users can delete own tasks"
  on tasks for delete
  to authenticated
  using (auth.uid() = user_id);

Выполни эти команды в SQL Editor.

💡 Про auth.uid():

auth.uid() возвращает ID текущего пользователя. Если пользователь не авторизован — возвращает null. Supabase автоматически знает кто делает запрос и подставляет правильный ID.

[ ШАГ 5: ПРОВЕРЯЕМ ЗАЩИТУ ]

Теперь проверим что защита работает.

Базовая проверка:

  1. Зайди в приложение. Видишь свои задачи? Должны появиться.
  2. Создай новую задачу. Она должна создаться.
  3. Зайди в Supabase → Table Editor. Посмотри на новую запись. В колонке user_id должен быть твой ID. Не NULL. ТВОЙ.

Проверка на проникновение:

Зайди в другом браузере (или в режиме инкогнито) под другим аккаунтом. Ты НЕ должен видеть задачи первого аккаунта.

Если видишь — ошибка в политике SELECT. Проверь что условие auth.uid() = user_id написано правильно.

Проверка Security Advisor:

В Supabase есть встроенный инструмент проверки безопасности.

  1. Открой Supabase → Database → Security Advisor
  2. Посмотри список предупреждений
  3. Если есть предупреждения про RLS — исправь

Security Advisor автоматически проверяет твою базу данных и показывает проблемы безопасности.

[ ЧАСТЫЕ ПРОБЛЕМЫ ]

"Я всё настроил, но ничего не вижу":

Проверь что ты авторизован. Если нет — auth.uid() вернёт null, и ты не увидишь записей (потому что user_id = null обычно не проходит проверку).

"Ошибка: new row violates row-level security policy":

Ты не передаёшь user_id при создании записи. Или передаёшь чужой ID.

В коде приложения должно быть:

// ОБЯЗАТЕЛЬНО передаём ID текущего пользователя
await supabase.from('tasks').insert({
  title: 'Купить хлеб',
  user_id: user.id  // <--- ВОТ ЭТО
})

"Вижу записи других пользователей":

Ошибка в политике SELECT. Проверь что условие auth.uid() = user_id написано правильно.

"RLS вообще не работает — вижу всё":

Проверь что ты не используешь service_role ключ в коде приложения.

service_role — это специальный ключ администратора. Он полностью обходит RLS. Его можно использовать только в серверном коде (Next.js API routes, Cloudflare Workers и т.д.), но НИКОГДА в браузере.

В приложении используй anon ключ (он в .env файле как SUPABASE_ANON_KEY).

[ ПЕРЕД ЗАПУСКОМ ]

Перед тем как запустить приложение для реальных пользователей, проверь:

  • RLS включён на ВСЕХ таблицах — даже на тех, которые кажутся "несекретными"
  • Security Advisor показывает 0 проблем — зелёный свет
  • Протестировал negative cases — попробовал получить чужие данные через Postman или curl напрямую (должно НЕ работать)
  • Проверил все операции: создание, чтение, обновление, удаление — всё работает только для своих записей
  • Использую "anon" ключ, а не "service_role" — в коде приложения должен быть только anon

Если используешь Storage (загрузка файлов):

Там тоже нужны RLS-политики. Это отдельная тема, но принцип тот же.

[ САМОПРОВЕРКА ]

Если ты молодец, то щас всё так:

  • В таблице есть user_id
  • Есть индекс на user_id (для скорости)
  • RLS включён (зелёная галочка в Supabase)
  • Политики созданы с to authenticated (видны в Table Editor → RLS Policies)
  • Ты видишь только свои записи
  • Другой пользователь видит только свои записи
  • Security Advisor показывает 0 проблем

Если верно — твоя база данных базово защищена. Не на миллион процентов, конечно, но уже больше чем у большинства вайбкодеров. Остальные нюансы впереди.

💬 Совет:

RLS это не та вещь которую настроил и забил. Это твой бронежилет. Если ты случайно выведешь "все задачи" в коде — RLS спасёт и покажет пользователю только его задачи. Поэтому удели этому вопросу много внимания.

Но если в RLS дыра — никакой код не спасёт. Всегда всё перепроверяй. Пытайся сломать. Будь параноиком. Это нормально и полезно!

Claude умеет писать RLS политики, но ты должен понимать что он делает и проверять результат. Security Advisor — тоже збс помощник. Используй его.