// docs / baas security / supabase storage
Supabase storage bucket security checklist: 22 আইটেম
Supabase Storage একটি S3-সামঞ্জস্যপূর্ণ bucket-এর চারপাশে একটি পাতলা wrapper এবং database-এর মতোই একই Row-Level Security model। অর্থাৎ যে একই RLS pitfalls tables-কে প্রভাবিত করে সেগুলি file access-কেও প্রভাবিত করে — এবং কিছু storage-specific যা AI কোডিং টুলগুলি uploads wire করার সময় উদিত হয়। এই checklist পাঁচটি section-এ 22 আইটেম: bucket configuration, RLS policies, upload validation, signed URLs, এবং operational hygiene। প্রতিটি 15 মিনিটের কম সময়ে verifiable।
নিচের প্রতিটি আইটেম অপরিহার্য। অন্তর্নিহিত RLS mechanics-এর জন্য, Supabase RLS scanner দেখুন। Storage-এর সংলগ্ন key-exposure class-এর জন্য, JavaScript-এ Supabase service role key উন্মোচিত দেখুন।
Bucket configuration
সঠিক defaults দিয়ে শুরু করুন। একটি misconfigured bucket files leak করে, আপনার RLS সঠিক হোক বা না হোক।
- প্রতিটি bucket default-এ private রাখুন। Supabase Dashboard → Storage → Buckets-এ, Public bucket toggle off-এ set করুন যতক্ষণ আপনার একটি স্পষ্ট কারণ না থাকে (marketing assets, PII ছাড়া public avatars)। Public buckets read operations-এর জন্য RLS bypass করে — bucket নাম সহ যে কেউ list এবং download করতে পারে।
- প্রতিটি bucket-এ একটি hard file size limit set করুন। Dashboard → Bucket settings → File size limit। User uploads-এর জন্য 50 MB একটি সংবেদনশীল default; video / large-file use cases-এর জন্য এটি ইচ্ছাকৃতভাবে বাড়ান। সীমা ছাড়া, একটি একক ক্ষতিকারক upload আপনার storage quota বা আপনার monthly bandwidth শেষ করতে পারে।
- প্রতি bucket allowed MIME types সীমাবদ্ধ করুন। Allowed MIME types list — স্পষ্ট allowlist, blocklist নয়। শুধুমাত্র-image bucket-এর জন্য
image/jpeg,image/png,image/webp। User-content bucket-এ কখনোtext/html,application/javascript, বাimage/svg+xmlঅনুমতি দেবেন না — signed URL-এর মাধ্যমে serve হলে সেগুলি browser-এ execute হয়। - একটি shared bucket নয়, প্রতি content type-এ একটি bucket ব্যবহার করুন। Per-bucket settings (size, MIME types, RLS policies) সেই granularity যা আপনার কাছে আছে। একটি
user-avatarsbucket, একটিdocument-uploadsbucket, এবং একটিpublic-assetsbucket একটি mixed bucket-এর চেয়ে lock down করা সহজ। - Frontend uploads হলে CORS configuration যাচাই করুন। যদি users browser থেকে সরাসরি signed URL-এ upload করে, bucket CORS-এ অবশ্যই আপনার production origin তালিকাভুক্ত থাকতে হবে।
*শুধুমাত্র public buckets-এর জন্য গ্রহণযোগ্য — user PII সহ buckets-এর জন্য কখনোই নয়।
storage.objects-এ RLS policies
Supabase Storage storage.objects table-এ file metadata সংরক্ষণ করে। সেই table-এ RLS নিয়ন্ত্রণ করে কে files পড়তে, upload করতে, update করতে, বা delete করতে পারে। RLS ছাড়া, bucket-এর public/private flag আপনার একমাত্র সুরক্ষা।
- Confirm করুন যে storage.objects-এ RLS enabled।
SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects';অবশ্যইtruereturn করতে হবে। Supabase নতুন projects-এ এটি default-এ enable করে; verify করুন যে এটি disabled হয়নি। - private buckets-এর জন্য
auth.uid()-এ scoped একটি SELECT policy লিখুন।CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);। প্রচলিত নিয়ম হল files-কে[user-id]/[filename]-এর অধীনে সংরক্ষণ করা এবং path থেকে owner বের করতেstorage.foldername()ব্যবহার করা। - একটি INSERT policy লিখুন যা একই path convention প্রয়োগ করে।
CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);। WITH CHECK ছাড়া, একজন authenticated user অন্য user-এর folder-এ upload করতে পারে। - আপনার অ্যাপ file edits বা deletes support করলে UPDATE এবং DELETE policies যোগ করুন। প্রতিটি command-এর নিজস্ব policy প্রয়োজন। DELETE skip করার অর্থ authenticated users তাদের নিজস্ব files সরাতে পারে না; UPDATE skip করার অর্থ file overwrites নীরবে fail হয়।
- দুটি browser session-এ cross-user access test করুন। User A হিসাবে sign in করুন, একটি file upload করুন, path copy করুন। অন্য browser-এ User B হিসাবে sign in করুন, REST API-এর মাধ্যমে file fetch করার চেষ্টা করুন। Response অবশ্যই
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]);Upload validation
প্রতিটি upload server-side validate করুন, এমনকি যখন bucket-এ MIME এবং size constraints থাকে। AI কোডিং টুলগুলি default-এ client-only validation তৈরি করে; এটি কিছুই রক্ষা করে না।
- File-এর প্রকৃত bytes থেকে server-side MIME type পুনরায় check করুন,
Content-Typeheader থেকে নয়।file-type(Node) বা magic-byte sniffing-এর মতো একটি library ব্যবহার করুন। একজন আক্রমণকারী এমন একটি file-এContent-Type: image/jpegদাবি করতে পারে যা আসলে একটি polyglot HTML / JavaScript payload। - Uploaded images থেকে EXIF metadata strip করুন। EXIF-এ GPS coordinates, device serial numbers, এবং timestamps থাকতে পারে। Storage-এর আগে strip করতে
.withMetadata(false)সহsharpবাexif-parserব্যবহার করুন। - যে SVGs-তে
scripttags বাonloadhandlers আছে সেগুলি reject করুন। SVG হল XML — এবং অনেক AI-generated অ্যাপ SVG uploads-কে "শুধু একটি image" হিসাবে অনুমতি দেয়। Server-sideDOMPurifyব্যবহার করুন বা সম্পূর্ণভাবে SVG uploads প্রত্যাখ্যান করুন। - Deterministic, unguessable filenames ব্যবহার করুন। মূল filename সংরক্ষণ করবেন না। একটি UUID বা file contents-এর একটি hash ব্যবহার করুন। মূল filenames leak করে ("
passport_scan_2024_01_15.jpg") এবং predictable names enumeration সক্ষম করে।
Signed URLs
Signed URLs হল clients কীভাবে private buckets অ্যাক্সেস করে। Expiry, bucket scope, এবং কী log হয় তা গুরুত্বপূর্ণ।
- Signed-URL expiry default 1 ঘণ্টা বা কম রাখুন। Supabase JS SDK-এর
createSignedUrl(path, expiresIn)seconds নেয়।31536000(এক বছর)-এর মতো মান কখনো ব্যবহার করবেন না — URL একটি স্থায়ী semi-public link হয়ে যায়। - Signed URLs কখনো আপনার database-এ সংরক্ষণ করবেন না। প্রতিটি request-এ server-side fresh ones তৈরি করুন। একটি 1-বছরের expiry সহ একটি stored signed URL যা database dump-এর মাধ্যমে leak হয় সেটি দীর্ঘমেয়াদী access দেয়।
- শুধু file uploads নয়, signed-URL generation log করুন। পরে compromise সন্দেহ করলে, আপনাকে জানতে হবে কে কখন কোন URL তৈরি করেছিল।
auth.uid()+ bucket + object path + timestamp log করুন। - User-uploaded files serve করার সময়
downloadAsoption ব্যবহার করুন।createSignedUrl(path, expiresIn, { download: '.jpg' })একটিContent-Disposition: attachmentheader force করে যাতে file render-এর পরিবর্তে download হয় — HTML / SVG / HTML-in-PDF execution class পরাজিত করে।
Operational hygiene
Storage configuration সময়ের সাথে সাথে drift করে। এই চারটি operational আইটেম পৃষ্ঠকে টাইট রাখে।
- ত্রৈমাসিকভাবে buckets audit করুন। Dashboard → Storage → Buckets। Confirm করুন public/private state এবং MIME-type lists অ্যাপ যা আশা করে তার সাথে মেলে। "অস্থায়ীভাবে" তৈরি buckets স্থায়ী হয়ে যায় যদি কেউ সেগুলি না সরায়।
- Anonymous list operations monitor করুন। Storage logs (Dashboard → Logs → Storage)
LISTrequests record করে। একটি private bucket-এর বিরুদ্ধে anonymous list requests-এর একটি spike মানে কেউ বাইরে থেকে এটি probe করছে। - Ephemeral uploads-এর জন্য একটি retention policy set করুন। Temp buckets (image preview, draft uploads) একটি scheduled function-এর মাধ্যমে 24-72 ঘণ্টা পরে auto-delete হওয়া উচিত। অনির্দিষ্ট retention GDPR / CCPA data-minimisation obligations-এর অধীনে একটি দায়।
- মাসিক একটি FixVibe scan চালান।
baas.supabase-storage-publiccheck যে buckets anonymousGET+LIST-এর প্রতিক্রিয়া জানায় সেগুলির জন্য probe করে। নতুন buckets যোগ হয়; পুরানো buckets visibility পরিবর্তন করে — শুধুমাত্র continuous scanning drift ধরে।
পরবর্তী পদক্ষেপ
আপনার production URL-এর বিরুদ্ধে একটি FixVibe scan চালান — anonymous storage listings baas.supabase-storage-public-এর অধীনে দেখায়। Table layer-এর জন্য এই checklist Supabase RLS scanner-এর সাথে এবং key-exposure সংলগ্নের জন্য JavaScript-এ Supabase service role key উন্মোচিত-এর সাথে যুক্ত করুন। অন্যান্য BaaS providers-এ storage misconfigurations-এর জন্য, BaaS misconfiguration scanner দেখুন।
