// docs / baas security / supabase storage
Чек-лист безпеки сховищ Supabase: 22 пункти
Supabase Storage — це тонка обгортка над S3-сумісним кошиком плюс та сама модель безпеки на рівні рядків, що й у бази даних. Це означає, що ті самі підводні камені RLS, які впливають на таблиці, впливають на доступ до файлів — та кілька специфічних для сховища, які з'являються, коли ШІ-інструменти кодування підключають завантаження. Цей чек-лист містить 22 пункти в п'яти розділах: конфігурація кошика, політики RLS, валідація завантаження, підписані URL та операційна гігієна. Кожен можна перевірити менш ніж за 15 хвилин.
Кожен пункт нижче — важливий. Для базової механіки RLS дивіться Сканер Supabase RLS. Для класу розкриття ключів, суміжного зі сховищем, дивіться Сервісний ключ Supabase, розкритий у JavaScript.
Конфігурація кошика
Починайте з правильних значень за замовчуванням. Неправильно налаштований кошик зливає файли незалежно від того, чи правильний ваш RLS.
- За замовчуванням робіть кожен кошик приватним. На Панелі Supabase → Storage → Buckets вимкніть перемикач Public bucket, якщо у вас немає явної причини (маркетингові ресурси, публічні аватари без PII). Публічні кошики обходять RLS для операцій читання — будь-хто з ім'ям кошика може перелічувати та завантажувати.
- Встановіть жорсткий ліміт розміру файлу для кожного кошика. Dashboard → Bucket settings → File size limit. 50 МБ — це розумне значення за замовчуванням для користувацьких завантажень; підвищуйте його свідомо для відео / завантажень великих файлів. Без ліміту одне зловмисне завантаження може вичерпати вашу квоту сховища або щомісячний трафік.
- Обмежте дозволені MIME-типи для кожного кошика. Список дозволених MIME-типів — явний allowlist, а не blocklist.
image/jpeg,image/png,image/webpдля кошиків лише з зображеннями. Ніколи не дозволяйтеtext/html,application/javascriptчиimage/svg+xmlу кошику з користувацьким контентом — вони виконуються в браузері, коли подаються через підписаний URL. - Використовуйте один кошик на тип контенту, а не один спільний кошик. Налаштування на рівні кошика (розмір, MIME-типи, політики RLS) — це гранулярність, яку ви маєте. Кошик
user-avatars, кошикdocument-uploadsта кошикpublic-assetsлегше заблокувати, ніж один змішаний кошик. - Перевірте конфігурацію CORS, якщо завантаження з фронтенду. Якщо користувачі завантажують безпосередньо з браузера на підписаний URL, CORS кошика має перелічувати ваш продакшен-origin.
*прийнятний лише для публічних кошиків — ніколи для кошиків, що містять користувацький PII.
Політики RLS на storage.objects
Supabase Storage зберігає метадані файлів у таблиці storage.objects. RLS на цій таблиці контролює, хто може читати, завантажувати, оновлювати чи видаляти файли. Без RLS прапорець public/private кошика — ваш єдиний захист.
- Підтвердіть, що RLS увімкнено на storage.objects.
SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects';має повернутиtrue. Supabase вмикає це за замовчуванням у нових проєктах; переконайтеся, що це не було вимкнено. - Напишіть політику 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()для витягання власника зі шляху. - Напишіть політику INSERT, яка нав'язує ту ж конвенцію шляху.
CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Без WITH CHECK автентифікований користувач може завантажувати в папку іншого користувача. - Додайте політики UPDATE та DELETE, якщо ваш застосунок підтримує редагування або видалення файлів. Кожна команда потребує своєї політики. Пропуск DELETE означає, що автентифіковані користувачі не можуть видалити свої власні файли; пропуск UPDATE означає, що перезаписи файлів мовчки зазнають невдачі.
- Перевірте міжкористувацький доступ у двох сесіях браузера. Увійдіть як Користувач А, завантажте файл, скопіюйте шлях. Увійдіть як Користувач Б в іншому браузері, спробуйте отримати файл через REST API. Відповідь має бути
403або404, ніколи200.
-- 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 та розміром. ШІ-інструменти кодування за замовчуванням генерують лише клієнтську валідацію; це нічого не захищає.
- Повторно перевіряйте MIME-тип на стороні сервера за фактичними байтами файлу, а не за заголовком
Content-Type. Використовуйте бібліотеку на кшталтfile-type(Node) або визначення за magic-байтами. Зловмисник може заявитиContent-Type: image/jpegна файлі, який насправді є поліглот-навантаженням HTML / JavaScript. - Видаляйте метадані EXIF із завантажених зображень. EXIF може містити GPS-координати, серійні номери пристроїв та часові мітки. Використовуйте
sharpіз.withMetadata(false)абоexif-parser, щоб видалити перед збереженням. - Відхиляйте SVG, які містять теги
scriptабо обробникиonload. SVG — це XML — і багато ШІ-згенерованих застосунків дозволяють завантаження SVG як "просто зображення". ВикористовуйтеDOMPurifyна стороні сервера або повністю відмовтеся від завантажень SVG. - Використовуйте детерміновані, невгадувані імена файлів. Не зберігайте оригінальне ім'я файлу. Використовуйте UUID або хеш вмісту файлу. Оригінальні імена файлів зливають інформацію ("
passport_scan_2024_01_15.jpg"), а передбачувані імена дозволяють перерахування.
Підписані URL
Підписані URL — це те, як клієнти отримують доступ до приватних кошиків. Термін дії, область кошика та що логується — має значення.
- За замовчуванням ставте термін дії підписаного URL на 1 годину або менше.
createSignedUrl(path, expiresIn)JS SDK Supabase приймає секунди. Ніколи не використовуйте значення на кшталт31536000(один рік) — URL стає постійним напівпублічним посиланням. - Ніколи не зберігайте підписані URL у вашій базі даних. Генеруйте свіжі на стороні сервера для кожного запиту. Збережений підписаний URL із річним терміном дії, що витекає через дамп бази даних, надає довготривалий доступ.
- Логуйте генерацію підписаних URL, а не лише завантаження файлів. Якщо ви пізніше підозрюєте компрометацію, вам потрібно знати, хто згенерував який URL і коли. Логуйте
auth.uid()+ кошик + шлях до об'єкта + часову мітку. - Використовуйте опцію
downloadAsпід час обслуговування завантажених користувачем файлів.createSignedUrl(path, expiresIn, { download: '.jpg' })примушує заголовокContent-Disposition: attachment, щоб файл завантажувався, а не рендерився — перемагає клас виконання HTML / SVG / HTML-в-PDF.
Операційна гігієна
Конфігурація сховища дрейфує з часом. Ці чотири операційні пункти тримають поверхню тісною.
- Проводьте аудит кошиків щоквартально. Dashboard → Storage → Buckets. Підтвердіть стан public/private та списки MIME-типів відповідають тому, що очікує застосунок. Кошики, створені "тимчасово", стають постійними, якщо ніхто їх не видалить.
- Моніторте анонімні операції переліку. Логи сховища (Dashboard → Logs → Storage) записують запити
LIST. Сплеск анонімних запитів переліку проти приватного кошика означає, що хтось зондує його ззовні. - Встановіть політику зберігання для ефемерних завантажень. Тимчасові кошики (перегляд зображень, чернетки завантажень) мають автоматично видалятися через 24-72 години через заплановану функцію. Безстрокове зберігання — це відповідальність згідно зі зобов'язаннями мінімізації даних GDPR / CCPA.
- Запускайте сканування FixVibe щомісяця. Перевірка
baas.supabase-storage-publicзондує кошики, які відповідають на анонімніGET+LIST. Додаються нові кошики; старі змінюють видимість — лише безперервне сканування ловить дрейф.
Наступні кроки
Запустіть сканування FixVibe проти вашого продакшен-URL — анонімні переліки сховищ з'являються під baas.supabase-storage-public. Поєднуйте цей чек-лист зі Сканер Supabase RLS для шару таблиць та Сервісний ключ Supabase, розкритий у JavaScript для суміжного класу розкриття ключів. Для неправильних налаштувань сховищ у інших постачальників BaaS дивіться Сканер неправильних налаштувань BaaS.
