Теперь настроим защиту на уровне базы данных. Пройдём все шаги по порядку.
Чтобы база данных знала кто владелец записи, нужна колонка user_id.
Сейчас в таблице tasks просто данные. Кто их владелец — неизвестно.
Добавим колонку user_id — это будет ID владельца записи.
Скажи Claude:
Claude напишет SQL примерно такой:
alter table tasks add column user_id uuid references auth.users(id);
Зайди в SQL Editor в Supabase и выполни этот запрос.
Теперь у каждой задачи (напоминаю: мы делаем веб-приложение для заметок, в качестве примера) может быть владелец (старые записи останутся пустые, но новые будут с владельцем).
Сейчас нужно добавить индекс на колонку user_id.
Что такое индекс?
Индекс это алфавитный указатель для базы данных. Он помогает быстро найти нужные строки.
Зачем это нужно:
Без индекса на user_id база данных будет проверять КАЖДУЮ строку в таблице: "это запись Васи? а это? а это?". На 10 000+ записей это может занять секунды. С индексом — мгновенно.
Скажи Claude:
Или выполни сам в SQL Editor:
create index tasks_user_id_idx on tasks(user_id);
Теперь поиск по владельцу будет быстрым.
Сейчас таблица открыта. Включим защиту.
alter table tasks enable row level security;
Важно: После выполнения этой команды все данные исчезнут из приложения.
Почему? Потому что RLS включён, но правил доступа ещё нет. По умолчанию правило одно: "Никому ничего". Даже тебе. Даже администратору. (Кроме service_role, но это особый ключ, он не для игр).
Они просто спрятались. Сейчас создадим правила доступа и данные вернутся.
Нужно объяснить базе данных кто что может делать.
Два типа проверки:
Важно:
Без WITH CHECK злоумышленник может создать запись с чужим user_id.
Тоже важно: роли
В Supabase есть две роли:
Мы разрешим действия только для authenticated — только для тех, кто вошёл.
Скажи Claude:
Код будет примерно такой:
-- ЧИТАТЬ только свои записи 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() возвращает ID текущего пользователя. Если пользователь не авторизован — возвращает null. Supabase автоматически знает кто делает запрос и подставляет правильный ID.
Теперь проверим что защита работает.
Базовая проверка:
user_id должен быть твой ID. Не NULL. ТВОЙ.Проверка на проникновение:
Зайди в другом браузере (или в режиме инкогнито) под другим аккаунтом. Ты НЕ должен видеть задачи первого аккаунта.
Если видишь — ошибка в политике SELECT. Проверь что условие auth.uid() = user_id написано правильно.
Проверка Security Advisor:
В Supabase есть встроенный инструмент проверки безопасности.
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).
Перед тем как запустить приложение для реальных пользователей, проверь:
anonЕсли используешь Storage (загрузка файлов):
Там тоже нужны RLS-политики. Это отдельная тема, но принцип тот же.
Если ты молодец, то щас всё так:
user_iduser_id (для скорости)to authenticated (видны в Table Editor → RLS Policies)Если верно — твоя база данных базово защищена. Не на миллион процентов, конечно, но уже больше чем у большинства вайбкодеров. Остальные нюансы впереди.
RLS это не та вещь которую настроил и забил. Это твой бронежилет. Если ты случайно выведешь "все задачи" в коде — RLS спасёт и покажет пользователю только его задачи. Поэтому удели этому вопросу много внимания.
Но если в RLS дыра — никакой код не спасёт. Всегда всё перепроверяй. Пытайся сломать. Будь параноиком. Это нормально и полезно!
Claude умеет писать RLS политики, но ты должен понимать что он делает и проверять результат. Security Advisor — тоже збс помощник. Используй его.