FixVibe

// docs / baas security / supabase storage

Чек-лист безпеки сховищ Supabase: 22 пункти

Supabase Storage — це тонка обгортка над S3-сумісним кошиком плюс та сама модель безпеки на рівні рядків, що й у бази даних. Це означає, що ті самі підводні камені RLS, які впливають на таблиці, впливають на доступ до файлів — та кілька специфічних для сховища, які з'являються, коли ШІ-інструменти кодування підключають завантаження. Цей чек-лист містить 22 пункти в п'яти розділах: конфігурація кошика, політики RLS, валідація завантаження, підписані URL та операційна гігієна. Кожен можна перевірити менш ніж за 15 хвилин.

Кожен пункт нижче — важливий. Для базової механіки RLS дивіться Сканер Supabase RLS. Для класу розкриття ключів, суміжного зі сховищем, дивіться Сервісний ключ Supabase, розкритий у JavaScript.

Конфігурація кошика

Починайте з правильних значень за замовчуванням. Неправильно налаштований кошик зливає файли незалежно від того, чи правильний ваш RLS.

  1. За замовчуванням робіть кожен кошик приватним. На Панелі Supabase → Storage → Buckets вимкніть перемикач Public bucket, якщо у вас немає явної причини (маркетингові ресурси, публічні аватари без PII). Публічні кошики обходять RLS для операцій читання — будь-хто з ім'ям кошика може перелічувати та завантажувати.
  2. Встановіть жорсткий ліміт розміру файлу для кожного кошика. Dashboard → Bucket settings → File size limit. 50 МБ — це розумне значення за замовчуванням для користувацьких завантажень; підвищуйте його свідомо для відео / завантажень великих файлів. Без ліміту одне зловмисне завантаження може вичерпати вашу квоту сховища або щомісячний трафік.
  3. Обмежте дозволені MIME-типи для кожного кошика. Список дозволених MIME-типів — явний allowlist, а не blocklist. image/jpeg, image/png, image/webp для кошиків лише з зображеннями. Ніколи не дозволяйте text/html, application/javascript чи image/svg+xml у кошику з користувацьким контентом — вони виконуються в браузері, коли подаються через підписаний URL.
  4. Використовуйте один кошик на тип контенту, а не один спільний кошик. Налаштування на рівні кошика (розмір, MIME-типи, політики RLS) — це гранулярність, яку ви маєте. Кошик user-avatars, кошик document-uploads та кошик public-assets легше заблокувати, ніж один змішаний кошик.
  5. Перевірте конфігурацію CORS, якщо завантаження з фронтенду. Якщо користувачі завантажують безпосередньо з браузера на підписаний URL, CORS кошика має перелічувати ваш продакшен-origin. * прийнятний лише для публічних кошиків — ніколи для кошиків, що містять користувацький PII.

Політики RLS на storage.objects

Supabase Storage зберігає метадані файлів у таблиці storage.objects. RLS на цій таблиці контролює, хто може читати, завантажувати, оновлювати чи видаляти файли. Без RLS прапорець public/private кошика — ваш єдиний захист.

  1. Підтвердіть, що RLS увімкнено на storage.objects. SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects'; має повернути true. Supabase вмикає це за замовчуванням у нових проєктах; переконайтеся, що це не було вимкнено.
  2. Напишіть політику SELECT, обмежену через auth.uid() для приватних кошиків. CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);. Конвенція — зберігати файли під [user-id]/[filename] та використовувати storage.foldername() для витягання власника зі шляху.
  3. Напишіть політику INSERT, яка нав'язує ту ж конвенцію шляху. CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Без WITH CHECK автентифікований користувач може завантажувати в папку іншого користувача.
  4. Додайте політики UPDATE та DELETE, якщо ваш застосунок підтримує редагування або видалення файлів. Кожна команда потребує своєї політики. Пропуск DELETE означає, що автентифіковані користувачі не можуть видалити свої власні файли; пропуск UPDATE означає, що перезаписи файлів мовчки зазнають невдачі.
  5. Перевірте міжкористувацький доступ у двох сесіях браузера. Увійдіть як Користувач А, завантажте файл, скопіюйте шлях. Увійдіть як Користувач Б в іншому браузері, спробуйте отримати файл через REST API. Відповідь має бути 403 або 404, ніколи 200.
sql
-- Confirm RLS on storage.objects
SELECT rowsecurity
FROM   pg_tables
WHERE  schemaname = 'storage' AND tablename = 'objects';

-- SELECT policy: scope reads to the owning user's folder.
CREATE POLICY "users_read_own_files"
  ON storage.objects
  FOR SELECT
  USING (auth.uid()::text = (storage.foldername(name))[1]);

-- INSERT policy: enforce the [user-id]/[filename] path convention.
CREATE POLICY "users_upload_own"
  ON storage.objects
  FOR INSERT
  WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);

Валідація завантажень

Валідуйте кожне завантаження на стороні сервера, навіть коли кошик має обмеження за MIME та розміром. ШІ-інструменти кодування за замовчуванням генерують лише клієнтську валідацію; це нічого не захищає.

  1. Повторно перевіряйте MIME-тип на стороні сервера за фактичними байтами файлу, а не за заголовком Content-Type. Використовуйте бібліотеку на кшталт file-type (Node) або визначення за magic-байтами. Зловмисник може заявити Content-Type: image/jpeg на файлі, який насправді є поліглот-навантаженням HTML / JavaScript.
  2. Видаляйте метадані EXIF із завантажених зображень. EXIF може містити GPS-координати, серійні номери пристроїв та часові мітки. Використовуйте sharp із .withMetadata(false) або exif-parser, щоб видалити перед збереженням.
  3. Відхиляйте SVG, які містять теги script або обробники onload. SVG — це XML — і багато ШІ-згенерованих застосунків дозволяють завантаження SVG як "просто зображення". Використовуйте DOMPurify на стороні сервера або повністю відмовтеся від завантажень SVG.
  4. Використовуйте детерміновані, невгадувані імена файлів. Не зберігайте оригінальне ім'я файлу. Використовуйте UUID або хеш вмісту файлу. Оригінальні імена файлів зливають інформацію ("passport_scan_2024_01_15.jpg"), а передбачувані імена дозволяють перерахування.

Підписані URL

Підписані URL — це те, як клієнти отримують доступ до приватних кошиків. Термін дії, область кошика та що логується — має значення.

  1. За замовчуванням ставте термін дії підписаного URL на 1 годину або менше. createSignedUrl(path, expiresIn) JS SDK Supabase приймає секунди. Ніколи не використовуйте значення на кшталт 31536000 (один рік) — URL стає постійним напівпублічним посиланням.
  2. Ніколи не зберігайте підписані URL у вашій базі даних. Генеруйте свіжі на стороні сервера для кожного запиту. Збережений підписаний URL із річним терміном дії, що витекає через дамп бази даних, надає довготривалий доступ.
  3. Логуйте генерацію підписаних URL, а не лише завантаження файлів. Якщо ви пізніше підозрюєте компрометацію, вам потрібно знати, хто згенерував який URL і коли. Логуйте auth.uid() + кошик + шлях до об'єкта + часову мітку.
  4. Використовуйте опцію downloadAs під час обслуговування завантажених користувачем файлів. createSignedUrl(path, expiresIn, { download: '.jpg' }) примушує заголовок Content-Disposition: attachment, щоб файл завантажувався, а не рендерився — перемагає клас виконання HTML / SVG / HTML-в-PDF.

Операційна гігієна

Конфігурація сховища дрейфує з часом. Ці чотири операційні пункти тримають поверхню тісною.

  1. Проводьте аудит кошиків щоквартально. Dashboard → Storage → Buckets. Підтвердіть стан public/private та списки MIME-типів відповідають тому, що очікує застосунок. Кошики, створені "тимчасово", стають постійними, якщо ніхто їх не видалить.
  2. Моніторте анонімні операції переліку. Логи сховища (Dashboard → Logs → Storage) записують запити LIST. Сплеск анонімних запитів переліку проти приватного кошика означає, що хтось зондує його ззовні.
  3. Встановіть політику зберігання для ефемерних завантажень. Тимчасові кошики (перегляд зображень, чернетки завантажень) мають автоматично видалятися через 24-72 години через заплановану функцію. Безстрокове зберігання — це відповідальність згідно зі зобов'язаннями мінімізації даних GDPR / CCPA.
  4. Запускайте сканування FixVibe щомісяця. Перевірка baas.supabase-storage-public зондує кошики, які відповідають на анонімні GET + LIST. Додаються нові кошики; старі змінюють видимість — лише безперервне сканування ловить дрейф.

Наступні кроки

Запустіть сканування FixVibe проти вашого продакшен-URL — анонімні переліки сховищ з'являються під baas.supabase-storage-public. Поєднуйте цей чек-лист зі Сканер Supabase RLS для шару таблиць та Сервісний ключ Supabase, розкритий у JavaScript для суміжного класу розкриття ключів. Для неправильних налаштувань сховищ у інших постачальників BaaS дивіться Сканер неправильних налаштувань BaaS.

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

Знайдіть відкриту таблицю раніше, ніж це зробить хтось інший.

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

  • Безкоштовний тариф — 3 сканування на місяць, без картки під час реєстрації.
  • Пасивне зняття відбитків BaaS — не потрібна перевірка володіння доменом.
  • Supabase, Firebase, Clerk, Auth0, Appwrite та інші.
  • Підказки виправлення від ШІ для кожної знахідки — вставте назад у Cursor / Claude Code.
Чек-лист безпеки сховищ Supabase: 22 пункти — Docs · FixVibe