FixVibe

// docs / baas security / supabase service role exposure

Ключ сервисной роли Supabase, раскрытый в JavaScript: что это значит и как его найти

Ключ сервисной роли Supabase — это мастер-ключ к вашей базе данных. Любой, кто его держит, обходит безопасность на уровне строк, может читать каждый столбец каждой таблицы и может писать или удалять всё, что захочет. Он разработан для существования исключительно в серверном коде — никогда в браузере. Когда ИИ-инструмент кодирования отправляет его в JavaScript-бандл, ваша база данных, по сути, становится публичной. Эта статья объясняет форму JWT, которая идентифицирует утёкший ключ, три ИИ-шаблона, которые производят утечку, что делать в первый час после обнаружения, и как автоматически сканировать на это до того, как это сделают пользователи.

Что такое ключ сервисной роли

Supabase выдаёт два разных ключа для каждого проекта: ключ anon (также называемый публикуемым ключом в более новых проектах) и ключ service_role. Оба являются веб-токенами JSON, подписанными секретом JWT вашего проекта. Разница заключается в утверждении role, встроенном в полезную нагрузку JWT — anon для публичного ключа, service_role для мастер-ключа. PostgREST, Supabase Storage и Supabase Auth все переключаются в режим обхода всего, когда видят утверждение service_role.

Декодируйте любой ключ Supabase на jwt.io и посмотрите на полезную нагрузку. Форма JWT сервисной роли безошибочна:

Декодированная полезная нагрузка JWT сервисной роли (показана как блок с подсветкой синтаксиса ниже).

json
{
  "iss": "supabase",
  "ref": "[project-ref]",
  "role": "service_role",
  "iat": 1700000000,
  "exp": 2000000000
}

Новые проекты Supabase выдают ключи в стиле секретов с префиксом sb_secret_ вместо JWT. Поведение идентично — всё, что несёт sb_secret_ в публичном бандле, одинаково катастрофично.

Как ИИ-инструменты кодирования утекают ключ сервисной роли

Мы видели одни и те же три шаблона в тысячах vibe-кодированных приложений. Каждый начинается с того, что разработчик просит ИИ-инструмент о помощи, и заканчивается тем, что сервисный ключ встраивается в бандл.

Шаблон 1: Один файл .env с префиксом NEXT_PUBLIC_

Разработчик просит ИИ-инструмент «настроить Supabase» и принимает один .env с обоими ключами. ИИ-инструмент — обученный на корпусе, где большинство переменных окружения предоставляются через NEXT_PUBLIC_* — добавляет префикс NEXT_PUBLIC_ к обоим. Next.js встраивает всё, что соответствует этому префиксу, в клиентский бандл во время сборки. Развёртывание на Vercel, и сервисный ключ находится в main.[hash].js.

Шаблон 2: Неправильный ключ в вызове createClient

Разработчик вставляет оба ключа в файл config.ts, который сгенерировал ИИ, и ИИ по ошибке заполняет вызов createClient() на стороне браузера с process.env.SUPABASE_SERVICE_ROLE_KEY. Сборка втягивает переменную, и JWT попадает в бандл.

Шаблон 3: Ключ сервисной роли захардкожен в скриптах заполнения

Разработчик просит ИИ-инструмент написать скрипт для заполнения базы данных. ИИ жёстко кодирует ключ сервисной роли непосредственно в файл (вместо чтения из окружения), коммитит файл в репозиторий, и публичный репозиторий GitHub или маршрут /scripts/seed.js развёрнутого приложения теперь обслуживает ключ.

Как сканирование бандла FixVibe обнаруживает утечку

Проверка секретов бандла FixVibe загружает каждый JavaScript-файл, на который ссылается развёрнутое приложение — входные чанки, лениво загружаемые чанки, веб-воркеры, сервис-воркеры — и пропускает их через детектор, который декодирует всё, соответствующее форме JWT (eyJ[base64-header].eyJ[base64-payload].[signature]). Если декодированная полезная нагрузка содержит "role": "service_role", сканирование сообщает об этом как о критической находке с путём к файлу и точной строкой, где появляется ключ. Та же проверка также сопоставляет более новый шаблон sb_secret_* по префиксу.

Сканирование никогда не аутентифицируется с обнаруженным ключом. Оно идентифицирует форму и сообщает об утечке — использование ключа для доказательства возможности эксплуатации было бы несанкционированным доступом к вашей базе данных. Доказательство находится в самой полезной нагрузке JWT.

Обнаружено — что делать в первый час

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

  1. Немедленно ротируйте ключ. В Панели Supabase перейдите в Project Settings → API → Service role key → Reset. Старый ключ становится недействительным в течение секунд. Любой серверный код, использующий ключ, должен быть обновлён и развёрнут заново до того, как ротация вступит в силу.
  2. Проверьте недавнюю активность базы данных. Откройте Database → Logs в панели. Отфильтруйте за последние 7 дней. Ищите необычные запросы SELECT * к таблицам с PII, большие операторы UPDATE или DELETE и запросы с IP-адресов за пределами вашей известной инфраструктуры. Supabase регистрирует заголовок x-real-ip для каждого запроса.
  3. Проверьте объекты хранилища. Посетите Storage → Logs и просмотрите недавние загрузки файлов. Утёкший ключ сервисной роли также даёт доступ с обходом всего и к приватным корзинам.
  4. Удалите ключ из системы контроля исходного кода. Даже после ротации, оставление JWT в истории git означает, что он обнаруживаем в публичном репозитории. Используйте git filter-repo или BFG Repo-Cleaner, чтобы очистить его из истории, затем выполните принудительный push (сначала предупредите коллег).
  5. Повторное сканирование после исправления. Запустите свежее сканирование FixVibe против повторно развёрнутого приложения. Находка секретов бандла должна очиститься. Подтвердите, что ни в одном чанке не остаётся JWT service_role и строки sb_secret_*.

Предотвращение утечки в первую очередь

Структурное исправление — это дисциплина именования плюс защитные механизмы на уровне инструментов:

  • Никогда не добавляйте к сервисному ключу префикс NEXT_PUBLIC_*, VITE_* или любой другой префикс встраивания в бандл. Соглашение об именовании — это граница — каждый фреймворк его уважает.
  • Держите сервисный ключ полностью вне .env на машине разработчика. Читайте его из менеджера секретов (Doppler, Infisical, зашифрованные переменные окружения Vercel) при развёртывании, никогда не коммитьте локально.
  • <strong>Mark every Supabase client construction with explicit context.</strong> Files named <code>supabase/browser.ts</code> use the anon key; files named <code>supabase/server.ts</code> use the service-role key with <code>import 'server-only'</code> at the top. The <code>server-only</code> import causes a build error if a client component tries to consume the module.
  • <strong>Add a pre-commit hook that greps for JWT-shaped strings.</strong> <code>git diff --staged | grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'</code> catches both anon and service tokens before they leave your machine.
  • Добавьте CI-гейт, который сканирует выходные данные сборки. После next build выполните grep вывода .next/static/chunks/ на строку service_role. Завалите сборку, если что-то совпадает.
bash
# Pre-commit hook: refuse any staged JWT-shaped string.
git diff --staged \
  | grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' \
  && echo "JWT detected in staged changes — refusing commit" \
  && exit 1

# CI gate: fail the build if "service_role" shipped to the static bundle.
grep -RE 'service_role|sb_secret_' .next/static/chunks/ \
  && echo "Service-role credential leaked into bundle" \
  && exit 1

Часто задаваемые вопросы

Как быстро злоумышленники на самом деле находят утёкшие ключи сервисной роли Supabase?

Сканеры публичных бандлов прочёсывают новые развёртывания в течение минут. Исследователи задокументировали работающие эксплойты против новых проектов Supabase в течение часа с первого развёртывания. Относитесь к любому раскрытию сервисной роли как к 60-минутному окну, а не 60-дневному.

Достаточно ли ротации ключа, или я должен предположить эксфильтрацию данных?

Ротация делает недействительным утёкший ключ, но не отменяет уже извлечённые данные. Если ваши таблицы содержат PII, платёжные данные или любые регулируемые данные, у вас может быть обязательство по уведомлению согласно GDPR (72 часа), CCPA или HIPAA. Проверьте журналы и проконсультируйтесь с юристом, если аудит показывает подозрительный доступ.

Может ли RLS защитить меня, если ключ сервисной роли утекает?

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

Применимо ли это к новой модели публикуемых / секретных ключей Supabase (<code>sb_publishable_</code> / <code>sb_secret_</code>)?

Да — идентичный класс риска. Ключ sb_secret_* — это новый формат секретного ключа, заменяющий JWT сервисной роли для новых проектов. Всё, что несёт sb_secret_* в бандле, так же катастрофично, как утёкший JWT сервисной роли. Детектор секретов бандла FixVibe сопоставляет обе формы.

А как насчёт ключа anon / публикуемого ключа — он безопасен в бандле?

Да, по дизайну. Ключ anon предназначен для жизни в браузере и является тем, что использует каждый веб-клиент Supabase. Его безопасность полностью зависит от того, правильно ли настроен RLS на каждой публичной таблице. См. статью Сканер Supabase RLS о том, что проверять.

Следующие шаги

Запустите сканирование FixVibe против вашего производственного URL — проверка секретов бандла бесплатна, без регистрации и сообщает о раскрытии service_role менее чем за минуту. Сочетайте это со статьёй Сканер Supabase RLS, чтобы проверить, что слой RLS выполняет свою работу, и Чек-лист безопасности хранилища Supabase, чтобы заблокировать доступ к файлам. Для понимания, почему ИИ-инструменты так надёжно генерируют этот класс утечки, прочитайте Почему ИИ-инструменты кодирования оставляют пробелы в безопасности.

// сканируйте вашу baas-поверхность

Найдите открытую таблицу раньше, чем это сделает кто-то другой.

Введите производственный URL. FixVibe перечислит поставщиков BaaS, с которыми взаимодействует ваше приложение, снимет отпечатки с их публичных конечных точек и сообщит, что неавторизованный клиент может прочитать или записать. Бесплатно, без установки, без карты.

  • Бесплатный тариф — 3 сканирования в месяц, без карты при регистрации.
  • Пассивное снятие отпечатков BaaS — не требуется проверка владения доменом.
  • Supabase, Firebase, Clerk, Auth0, Appwrite и другие.
  • Подсказки исправления ИИ для каждой находки — вставьте обратно в Cursor / Claude Code.
Ключ сервисной роли Supabase, раскрытый в JavaScript: что это значит и как его найти — Docs · FixVibe