[Перевод] Я завайбкодил и запустил приложение за три дня. И его взломали. Дважды. Вот что я усвоил

Моё приложение представляет собой каталог, который объединяет профили исследователей безопасности с различных платформ, таких как HackerOne, Bugcrowd, GitHub и других. Фронтенд подключается к Supabase, и я создал его с помощью инструментов Lovable и Cursor. Вся автоматизация, которая подает данные в базу данных, обрабатывается отдельно.

Изначально я планировал разрешить пользователям самостоятельно регистрироваться через Supabase Auth и запрашивать профили, которые они хотели бы агрегировать. Но в процессе реализации я понял: это создаёт риски, ведь нужно корректно управлять как аутентификацией (какой именно пользователь), так и авторизацией (что ему разрешено). Я отказался от саморегистрации… но упустил важную деталь. (Подробнее об этом чуть позже.)

Баг №1: Утечка email-адресов

На первом тесте пользователи проходили аутентификацию через Supabase, что требовало реальных email-адресов. При анализе сетевых запросов я заметил, что ответы от таблицы profiles содержали email-поля пользователей. Это очевидно плохо, особенно если бы кто-то действительно доверил мне личные данные.

Чтобы исправить это, я создал PostgreSQL-представление, которое запрашивало только нужные мне поля из профилей, исключая email. Фронтенд начал запрашивать данные через это представление. Казалось, стало безопаснее… но на самом деле — только хуже. (Скоро поймёте почему.)

Запуск и первая компрометация

После перехода на представление и удаления возможности добавления данных с фронтенда, я запустил приложение. У меня была включена безопасность на уровне строк (Row-Level Security, RLS), и я считал, что доступ «только для чтения» сделает систему безопасной. Однако через 24 часа исследователь по безопасности показал, что можно вставлять, обновлять и удалять записи, хотя фронтенд не предоставлял такие эндпоинты, а RLS должна была это блокировать.

Почему это сработало? Проблема была в том, что созданное мной представление игнорировало RLS.

По умолчанию PostgreSQL-представления выполняются с привилегиями владельца. В моём случае — это была админская роль. Если не указать SECURITY INVOKER или не настроить доп. защиту, политики RLS таблиц обходятся. Это и стало критической ошибкой конфигурации. Я исправил это, отозвав небезопасный доступ к представлению и перестроив логику запросов данных. Респект исследователю, который ответственно сообщил об уязвимости — @Goofygiraffe06.

Вторая компрометация

На следующий день другой исследователь (@Kr1shna4garwal) сообщил: он смог создать новые профили в базе данных. К счастью, он не мог редактировать или удалять существующие. Причина была не в представлении таблицы. Я просто забыл отключить Supabase Auth, хотя уже убрал регистрацию из фронтенда. Это означало, что злоумышленники всё ещё могли регистрироваться через email/password, получая роль аутентифицированного пользователя — и доступ в рамках RLS. Я отключил регистрацию в настройках Supabase Auth. Проблема решена.

Главное, что я вынес из этого:

  • Вайбкодинг позволяет быстро запускать продукты, но дыры в безопасности — это норма по умолчанию.

  • Supabase и PostgreSQL — мощные инструменты, но их легко настроить неправильно, если не разбираться в модели безопасности, особенно в работе представлений и RLS.

  • Если используете представления PostgreSQL, помните: они не учитывают политики RLS по умолчанию.

  • Отключайте Supabase Auth, если вы им не пользуетесь — даже если интерфейс не показывает такой возможности.

  • Threat modeling — обязателен. Моё приложение работает только с публичными данными, поэтому ущерб был минимален. Но если бы речь шла о персональных данных (PII) или медицинской информации (PHI), такая уязвимость могла бы стать катастрофой.