00:00:00В этом месяце вышел отчет: сканирование 20 000 инди-приложений показало, что в каждом девятом
00:00:04учетные данные Supabase были открыты прямо во фронтенд-коде.
00:00:08И речь не о логах сервера или приватных репозиториях.
00:00:11Они находятся в обычном JavaScript, который скачивает каждый посетитель.
00:00:14Причем эти приложения никто не взламывал — разработчики просто случайно выложили секрет в открытый доступ.
00:00:18Поясню: некоторые ключи и должны быть публичными.
00:00:21Проблема в самом процессе разработки, ведь из-за той же ошибки в браузер может попасть ключ,
00:00:25которому там вообще не место.
00:00:27У нас постоянно выходят новые видео.
00:00:28Не забудьте подписаться.
00:00:35Вот простая истина, которая многих ставит в тупик.
00:00:38Мы все об этом догадываемся, но в спешке об этом легко забыть.
00:00:41Когда вы помечаете переменную окружения как публичную, ваш сборщик считает, что она нужна
00:00:46в браузере, и добавляет ее в клиентский бандл.
00:00:50Next.js делает это через NEXT_PUBLIC, Vite — через VITE_, а SvelteKit использует
00:00:56префикс public.
00:00:57Слово “public” — это не метка безопасности, это буквально наклейка “отправить клиенту”.
00:01:01Теперь разберемся с особенностями Supabase.
00:01:04Одни ключи должны быть публичными (как anon или publishable), а другие — приватными,
00:01:10например service_role или секретные ключи.
00:01:12Если приватный ключ попадает в браузер... ну, вы сами понимаете, что будет.
00:01:17В этом случае Row Level Security (RLS) вас не спасет.
00:01:20В документации Supabase четко сказано, что сервисные ключи обходят RLS.
00:01:24Так что если этот ключ во фронтенде, вся ваша работа над политиками доступа теряет смысл.
00:01:29Позвольте мне показать на практике в моей «песочнице», как именно это происходит.
00:01:33Сначала я создал простое CRUD-приложение на Next.js и подключил к нему Supabase.
00:01:38Вот мой .env файл, и вот она — ошибка.
00:01:41Я поместил ключ в переменную с префиксом PUBLIC.
00:01:45Этот ключ публичный, так что он и должен быть виден.
00:01:48Но проблема в том, что через тот же NEXT_PUBLIC можно случайно отправить и приватный ключ.
00:01:53Я запустил npm run build, собрал проект и запустил приложение.
00:01:59Открываю Chrome.
00:02:00Быстро добавлю данные в нашу базу через приложение.
00:02:05Теперь я открываю скомпилированный JavaScript-бандл и ищу этот ключ.
00:02:10Вот он.
00:02:12URL и ключ лежат прямо внутри файла, который ваши пользователи только что загрузили
00:02:18в свой браузер.
00:02:19И заметьте: никакого взлома.
00:02:21Я просто нашел его, понимаете?
00:02:22Никто не использовал уязвимости.
00:02:24Это просто чтение того, что приложение само выложило в интернет.
00:02:25Если это видите вы, это увидит любой.
00:02:29Откройте инструменты разработчика, изучите JS-файлы и введите поиск.
00:02:32Больше ничего не нужно.
00:02:35И если вы надеетесь, что «никто не будет смотреть» — интернет полон людей и ботов,
00:02:36чья работа как раз и заключается в поиске таких вещей.
00:02:41Вот решение, которое действительно работает.
00:02:44Браузер должен обращаться только к вашему API.
00:02:47Ваш API работает на стороне сервера.
00:02:50Именно там должны храниться приватные ключи.
00:02:52Перенесите приватные операции в API-роуты или серверные функции.
00:02:54Клиент вызывает ваш эндпоинт, ваш эндпоинт обращается к Supabase. Затем пересоберите проект и проверьте бандл.
00:02:58Если ключ исчез из бандла — вы исправили проблему. Но не стоит на этом останавливаться,
00:03:03есть и другие меры предосторожности.
00:03:04Убедитесь, что RLS включен для всех таблиц, к которым обращается пользователь,
00:03:09и проверьте, что ваши политики работают именно так, как задумано.
00:03:11Уделите время тестированию.
00:03:16Мне кажется, об этом часто забывают.
00:03:18Теперь о том, как не допустить повторения ошибки. Обычно разработчики исправляют это один раз,
00:03:19а потом в спешке снова наступают на те же грабли.
00:03:21Установите «ограждения». Начните со сканирования секретов в CI,
00:03:26чтобы сборка падала, если ключ окажется там, где не должен.
00:03:28Внедрите правило при проверке PR: всё, что помечено NEXT_PUBLIC или VITE, по умолчанию считается публичным.
00:03:34И наконец, настройте ротацию ключей.
00:03:36Если есть хоть малейшее подозрение, что ключи утекли — просто смените их.
00:03:41Это лучше, чем гадать несколько дней, воспользуется ли ими кто-то.
00:03:42Что вы можете сделать прямо сейчас:
00:03:43Соберите приложение так, как вы его деплоите.
00:03:47Поищите в результатах сборки упоминания Supabase, JWT, service_secret или токены.
00:03:50Если нашли что-то приватное — считайте, что ключ скомпрометирован.
00:03:52Замените его и перенесите логику на сервер.
00:03:55Если из этого видео нужно запомнить только одну фразу, пусть это будет эта:
00:04:01Если ключ попал в бандл — он стал публичным.
00:04:05Увидимся в следующих видео.
00:04:08If you remember one line from this video, make it this.
00:04:11If it's in the bundle, it's public.
00:04:13We'll see you in another video.