Supabase Storage Security Checklist
Supabase Storage provides S3-compatible object storage with RLS integration. Misconfigured buckets are a common source of data leaks, especially when storing user-uploaded content like profile pictures, documents, or media files.
1. Bucket Classification
Supabase Storage supports two bucket types:
- Public buckets: Files can be accessed by anyone with the URL. No authentication required. Suitable for public assets like logos or marketing images.
- Private buckets: Files require authenticated access through RLS policies. Required for user uploads, documents, and any sensitive content.
Audit every bucket and confirm its classification:
SELECT id, name, public FROM storage.buckets;
If a bucket is public, verify that it truly should be. A common mistake is creating a public bucket for "convenience" during development and forgetting to change it.
2. RLS Policies on storage.objects
Storage access is controlled through RLS policies on the storage.objects table. Without explicit policies on a private bucket, no one can access the files (secure default). However, overly broad policies are dangerous:
-- DANGEROUS: Anyone can read all files in the bucket
CREATE POLICY "Public read" ON storage.objects
FOR SELECT USING (bucket_id = 'uploads');
-- CORRECT: Users can only read their own files
CREATE POLICY "User read own files" ON storage.objects
FOR SELECT USING (
bucket_id = 'uploads'
AND auth.uid()::text = (storage.foldername(name))[1]
);
The convention is to organize files into folders named after the user's UUID: uploads/{user_id}/filename.jpg. This makes RLS policies straightforward and predictable.
3. Upload Restrictions
Without restrictions, attackers can:
- Upload extremely large files to consume storage quota and increase costs.
- Upload executable files or HTML files that could be served as web pages (stored XSS).
- Upload files with malicious names to attempt path traversal.
Enforce restrictions in your RLS policy and application code:
-- Restrict uploads to images under 5MB
CREATE POLICY "Users can upload images" ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'avatars'
AND auth.uid()::text = (storage.foldername(name))[1]
AND (storage.extension(name)) IN ('jpg', 'jpeg', 'png', 'webp')
AND (metadata->>'size')::int < 5242880
);
Additionally, validate MIME types server-side. Do not trust the Content-Type header from the client. Use magic byte detection when possible.
4. File Name Validation
Sanitize file names to prevent:
- Path traversal: Names containing
../could escape the intended folder. - Special characters: Names with null bytes, newlines, or unicode tricks can cause issues.
- Name collisions: Use UUIDs or hash-based names to prevent one user from overwriting another user's file.
// Generate a safe file name
const safeName = `${crypto.randomUUID()}.${extension}`;
const path = `${user.id}/${safeName}`;
await supabase.storage.from('uploads').upload(path, file);
5. Signed URLs for Private Content
For private files that need to be shared temporarily (e.g., a download link in an email), use signed URLs with a short expiration:
const { data } = await supabase.storage
.from('documents')
.createSignedUrl('user-123/contract.pdf', 3600); // 1 hour expiry
Never generate signed URLs with excessively long expiration times. If a signed URL leaks, it grants access to anyone until it expires.
6. CORS Configuration
Configure CORS headers on your Supabase project to restrict which domains can make Storage API requests. This prevents malicious websites from using a logged-in user's session to upload or download files.
7. Monitoring and Lifecycle
- Monitor storage usage per bucket and set up alerts for unusual growth (potential abuse).
- Implement lifecycle rules or background jobs to clean up temporary uploads (e.g., incomplete multipart uploads, expired content).
- Log all upload and download events for audit purposes.
8. Content-Disposition Headers
When serving user-uploaded files, set Content-Disposition: attachment to prevent browsers from rendering uploaded HTML or SVG files inline, which could lead to stored XSS attacks.
Use AuditYour.app to scan your Supabase project for open storage buckets and misconfigured storage policies.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free Scan