SupabaseHigh

Writable Storage Bucket

Storage bucket allows unauthenticated users to upload, overwrite, or delete files without any access control.

Last updated 2026-01-15

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 Scan