What Is This Vulnerability
A writable storage bucket allows users to upload, overwrite, or delete files without proper authentication or authorization. This occurs when RLS policies on storage.objects grant INSERT, UPDATE, or DELETE access to the anonymous role, or when authenticated users can write to any path in the bucket regardless of ownership.
-- VULNERABLE: Anyone can upload files
CREATE POLICY "Public upload"
ON storage.objects FOR INSERT
WITH CHECK (bucket_id = 'uploads');
-- VULNERABLE: Anyone can delete files
CREATE POLICY "Public delete"
ON storage.objects FOR DELETE
USING (bucket_id = 'uploads');
Why It's Dangerous
Unrestricted write access to storage buckets creates severe risks:
- Malware hosting: Attackers upload malware, phishing pages, or illegal content using your infrastructure and domain
- Storage cost explosion: Automated uploads of large files can exhaust your storage quota and drive up costs rapidly
- File overwriting: Existing legitimate files can be replaced with malicious content or defaced
- Content injection: If uploaded files are served to other users, attackers can inject XSS payloads via SVG or HTML files
- Data destruction: Deleting other users' uploaded files permanently removes their data
- Legal liability: Your domain hosting illegal content creates legal and reputational risk
# Attacker uploads a malicious file
curl -X POST "https://YOUR_PROJECT.supabase.co/storage/v1/object/uploads/malware.exe" \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY" \
--data-binary @malware.exe
How to Detect
Test upload capability as an anonymous user:
// Test anonymous upload
const { error } = await supabase.storage
.from('uploads')
.upload('test-anonymous.txt', new Blob(['test']), {
contentType: 'text/plain',
});
if (!error) console.log('BUCKET IS WRITABLE BY ANONYMOUS USERS');
Review storage policies:
SELECT policyname, cmd, qual, with_check, roles
FROM pg_policies
WHERE schemaname = 'storage'
AND tablename = 'objects'
AND cmd IN ('INSERT', 'UPDATE', 'DELETE');
AuditYourApp attempts to upload a small test file to each bucket as an unauthenticated user and as an authenticated user outside the expected ownership path.
How to Fix
Restrict uploads to authenticated users and enforce folder-based ownership:
-- Drop permissive policies
DROP POLICY "Public upload" ON storage.objects;
DROP POLICY "Public delete" ON storage.objects;
-- Users can only upload to their own folder
CREATE POLICY "Users can upload to own folder"
ON storage.objects FOR INSERT
TO authenticated
WITH CHECK (
bucket_id = 'uploads'
AND (storage.foldername(name))[1] = auth.uid()::text
);
-- Users can only delete their own files
CREATE POLICY "Users can delete own files"
ON storage.objects FOR DELETE
TO authenticated
USING (
bucket_id = 'uploads'
AND (storage.foldername(name))[1] = auth.uid()::text
);
Additionally, set file size limits and allowed MIME types in your bucket configuration:
UPDATE storage.buckets
SET file_size_limit = 5242880, -- 5MB max
allowed_mime_types = ARRAY['image/jpeg', 'image/png', 'application/pdf']
WHERE name = 'uploads';
For applications that need anonymous uploads (e.g., a public submission form), use an Edge Function as a proxy that validates file type, size, and content before uploading with the service_role key.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free ScanRelated
vulnerabilities
Public Storage Bucket Exposure
Supabase storage buckets configured as public allow anyone to access uploaded files without authentication.
vulnerabilities
Listable Storage Bucket
Storage bucket contents can be enumerated by anonymous or authenticated users, revealing file names and structure.