What Is This Vulnerability
Supabase Storage allows you to create buckets that are either public or private. When a bucket is marked as public, any file within it can be accessed via a direct URL without any authentication. The URL format is predictable: https://YOUR_PROJECT.supabase.co/storage/v1/object/public/BUCKET_NAME/FILE_PATH. If the bucket contains sensitive files, this means anyone who can guess or enumerate file paths can download them.
Public buckets are appropriate for assets like profile pictures or public documents, but developers often use public buckets for convenience and inadvertently store sensitive files in them.
-- Check bucket configuration
SELECT id, name, public
FROM storage.buckets;
Why It's Dangerous
Public storage bucket exposure can lead to:
- Data breach: Sensitive documents, ID cards, medical records, or contracts are downloadable by anyone
- Intellectual property theft: Proprietary files, source code archives, or design assets can be stolen
- Privacy violations: User-uploaded photos, private messages with attachments, or personal files are exposed
- Compliance failures: Exposing PII or health data violates GDPR, HIPAA, and other regulations
- Reconnaissance: Attackers can download configuration files, database exports, or backup files that were carelessly stored
The predictable URL structure means files can be discovered through brute-force enumeration, especially when filenames follow patterns like UUIDs or sequential IDs.
How to Detect
Check your bucket configuration in the Supabase Dashboard under Storage, or query directly:
SELECT id, name, public, created_at
FROM storage.buckets
WHERE public = true;
Test access by constructing a URL to a known file:
curl -I "https://YOUR_PROJECT.supabase.co/storage/v1/object/public/BUCKET_NAME/test-file.pdf"
# If 200 OK, the file is publicly accessible
AuditYourApp checks all storage buckets for public access and attempts to list and download files. It flags any bucket containing files that appear sensitive based on file type and naming patterns.
How to Fix
Convert sensitive buckets to private and use signed URLs for access:
-- Make a bucket private
UPDATE storage.buckets
SET public = false
WHERE name = 'user-documents';
Then generate signed URLs server-side with an expiration time:
// In an Edge Function or server-side code
const { data } = await supabaseAdmin.storage
.from('user-documents')
.createSignedUrl('path/to/file.pdf', 3600); // Expires in 1 hour
Add RLS policies to the storage.objects table to control who can access files:
CREATE POLICY "Users can access own files"
ON storage.objects FOR SELECT
TO authenticated
USING (
bucket_id = 'user-documents'
AND (storage.foldername(name))[1] = auth.uid()::text
);
Organize files into user-specific folders (e.g., user-documents/{user_id}/file.pdf) so that RLS policies can enforce ownership based on the folder path. Keep a separate public bucket only for truly public assets like logos and marketing images.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free Scan