// docs / baas security / supabase storage
چکلیست امنیت سطل ذخیرهسازی Supabase: ۲۲ مورد
Supabase Storage یک پوشش نازک روی یک سطل سازگار با S3 بهعلاوه همان مدل امنیت در سطح ردیف مانند پایگاهداده است. این یعنی همان دامهای RLS که جدولها را تحت تأثیر قرار میدهند، دسترسی فایل را نیز تحت تأثیر قرار میدهند — و چند مورد خاص ذخیرهسازی که وقتی ابزارهای کدنویسی هوش مصنوعی آپلودها را پیکربندی میکنند ظاهر میشوند. این چکلیست ۲۲ مورد در پنج بخش است: پیکربندی سطل، سیاستهای RLS، اعتبارسنجی آپلود، signed URLها و بهداشت عملیاتی. هر مورد در کمتر از ۱۵ دقیقه قابل تأیید است.
هر مورد زیر ضروری است. برای مکانیکهای زیربنایی RLS، اسکنر Supabase RLS را ببینید. برای کلاس افشای کلید مجاور به ذخیرهسازی، افشای کلید service role Supabase در جاوااسکریپت را ببینید.
پیکربندی سطل
با پیشفرضهای درست شروع کنید. یک سطل با پیکربندی نادرست فایلها را افشا میکند چه RLS شما درست باشد چه نباشد.
- پیشفرض هر سطل را روی خصوصی قرار دهید. در داشبورد Supabase → Storage → Buckets، دکمه Public bucket را خاموش کنید مگر اینکه دلیل صریحی داشته باشید (داراییهای بازاریابی، آواتارهای عمومی بدون PII). سطلهای عمومی RLS را برای عملیات خواندن دور میزنند — هر کسی با نام سطل میتواند فهرست و دانلود کند.
- روی هر سطل یک حد سخت اندازه فایل تنظیم کنید. Dashboard → Bucket settings → File size limit. ۵۰ MB یک پیشفرض معقول برای آپلودهای کاربر است؛ آن را عمداً برای موارد کاربرد ویدئو / فایلهای بزرگ بالا ببرید. بدون حد، یک آپلود مخرب میتواند سهمیه ذخیرهسازی یا پهنای باند ماهانه شما را تخلیه کند.
- MIME typeهای مجاز را بهازای هر سطل محدود کنید. فهرست MIME typeهای مجاز — لیست سفید صریح، نه لیست سیاه.
image/jpeg،image/png،image/webpبرای سطلهای فقط-تصویر. هرگزtext/html،application/javascriptیاimage/svg+xmlرا در یک سطل محتوای کاربر اجازه ندهید — آنها وقتی از طریق signed URL ارائه میشوند در مرورگر اجرا میگردند. - یک سطل برای هر نوع محتوا استفاده کنید، نه یک سطل مشترک. تنظیمات per-bucket (اندازه، MIME typeها، سیاستهای RLS) دانهبندیای است که در اختیار دارید. یک سطل
user-avatars، یک سطلdocument-uploadsو یک سطلpublic-assetsراحتتر از یک سطل مخلوط قفل میشوند. - اگر فرانتاند آپلود میکند، پیکربندی CORS را تأیید کنید. اگر کاربران مستقیماً از مرورگر به یک signed URL آپلود میکنند، CORS سطل باید origin تولیدی شما را فهرست کند.
*فقط برای سطلهای عمومی قابل قبول است — هرگز برای سطلهای حاوی PII کاربر.
سیاستهای RLS روی storage.objects
Supabase Storage متادیتای فایل را در جدول storage.objects ذخیره میکند. RLS روی آن جدول کنترل میکند چه کسی میتواند فایلها را بخواند، آپلود کند، بهروزرسانی کند یا حذف کند. بدون RLS، پرچم عمومی/خصوصی سطل تنها محافظ شماست.
- تأیید کنید 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 یعنی بازنویسی فایل بیسروصدا شکست میخورد.
- دسترسی متقابل کاربران را در دو نشست مرورگر آزمایش کنید. بهعنوان کاربر A وارد شوید، یک فایل آپلود کنید، مسیر را کپی کنید. بهعنوان کاربر B در مرورگر دیگری وارد شوید، تلاش کنید فایل را از طریق 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 type را در سمت سرور از بایتهای واقعی فایل دوباره بررسی کنید، نه از هدر
Content-Type. از کتابخانهای مانندfile-type(Node) یا magic-byte sniffing استفاده کنید. یک مهاجم میتواندContent-Type: image/jpegرا روی فایلی که در واقع یک payload چندزبانه HTML / JavaScript است ادعا کند. - متادیتای EXIF را از تصاویر آپلودی پاک کنید. EXIF میتواند شامل مختصات GPS، شمارههای سریال دستگاه و timestampها باشد. از
sharpبا.withMetadata(false)یاexif-parserبرای پاک کردن پیش از ذخیرهسازی استفاده کنید. - SVGهایی که حاوی تگ
scriptیا هندلرonloadهستند را رد کنید. SVG یک XML است — و بسیاری از اپلیکیشنهای تولیدشده توسط هوش مصنوعی آپلود SVG را بهعنوان "فقط یک تصویر" اجازه میدهند. ازDOMPurifyدر سمت سرور استفاده کنید یا آپلود SVG را کاملاً رد کنید. - از نامفایلهای قطعی و غیرقابلحدس استفاده کنید. نامفایل اصلی را حفظ نکنید. از UUID یا هش محتوای فایل استفاده کنید. نامهای اصلی نشت میکنند ("
passport_scan_2024_01_15.jpg") و نامهای قابل پیشبینی شمارش را امکانپذیر میکنند.
Signed URLها
Signed URLها روشی هستند که کلاینتها به سطلهای خصوصی دسترسی پیدا میکنند. انقضا، دامنه سطل و آنچه ثبت میشود مهم است.
- انقضای پیشفرض signed URL را روی ۱ ساعت یا کمتر تنظیم کنید.
createSignedUrl(path, expiresIn)در Supabase JS SDK ثانیه میگیرد. هرگز از مقادیری مانند31536000(یک سال) استفاده نکنید — URL تبدیل به یک لینک نیمهعمومی دائمی میشود. - هرگز signed URLها را در پایگاهداده خود ذخیره نکنید. در هر درخواست در سمت سرور تازه تولید کنید. یک signed URL ذخیرهشده با انقضای ۱ ساله که از طریق یک dump پایگاهداده نشت میکند، دسترسی بلندمدت میدهد.
- تولید signed URL را ثبت کنید، نه فقط آپلودهای فایل. اگر بعداً به یک compromise مشکوک شدید، باید بدانید چه کسی چه URLی را چه زمانی تولید کرده.
auth.uid()+ سطل + مسیر شیء + timestamp را ثبت کنید. - هنگام ارائه فایلهای آپلودشده توسط کاربر از گزینه
downloadAsاستفاده کنید.createSignedUrl(path, expiresIn, { download: '.jpg' })یک هدرContent-Disposition: attachmentرا اجبار میکند تا فایل بهجای رندر شدن دانلود شود — کلاس اجرای HTML / SVG / HTML-in-PDF را شکست میدهد.
بهداشت عملیاتی
پیکربندی ذخیرهسازی در طول زمان از مسیر منحرف میشود. این چهار مورد عملیاتی سطح را محکم نگه میدارند.
- سطلها را بهصورت فصلی بررسی کنید. Dashboard → Storage → Buckets. تأیید کنید وضعیت عمومی/خصوصی و فهرستهای MIME-type با آنچه اپلیکیشن انتظار دارد مطابقت دارد. سطلهایی که "موقتاً" ایجاد شدهاند اگر کسی آنها را حذف نکند دائمی میشوند.
- عملیات لیست ناشناس را پایش کنید. لاگهای ذخیرهسازی (Dashboard → Logs → Storage) درخواستهای
LISTرا ثبت میکنند. جهش درخواستهای لیست ناشناس علیه یک سطل خصوصی یعنی کسی آن را از بیرون میکاود. - برای آپلودهای موقت یک سیاست نگهداری تنظیم کنید. سطلهای موقت (پیشنمایش تصویر، آپلودهای پیشنویس) باید پس از ۲۴-۷۲ ساعت از طریق یک تابع زمانبندیشده بهصورت خودکار حذف شوند. نگهداری نامحدود طبق تعهدات کاهش داده GDPR / CCPA یک مسئولیت است.
- یک اسکن FixVibe ماهانه اجرا کنید. فحص
baas.supabase-storage-publicسطلهایی را که بهGET+LISTناشناس پاسخ میدهند میکاود. سطلهای جدید اضافه میشوند؛ سطلهای قدیمی قابلیت رؤیت تغییر میدهند — فقط اسکن مداوم این انحراف را میگیرد.
گامهای بعدی
یک اسکن FixVibe را روی URL تولیدی خود اجرا کنید — فهرستهای ذخیرهسازی ناشناس زیر baas.supabase-storage-public ظاهر میشوند. این چکلیست را با اسکنر Supabase RLS برای لایه جدول و افشای کلید service role Supabase در جاوااسکریپت برای مجاورت افشای کلید ترکیب کنید. برای پیکربندیهای نادرست ذخیرهسازی در سایر ارائهدهندگان BaaS، اسکنر پیکربندی نادرست BaaS را ببینید.
