Supabase and Firebase Security: RLS and Rules Done Right
Backend-as-a-service platforms let the frontend talk to the database directly. That is fast to build with, and it means your database is effectively a public API. The only thing standing between an attacker and your data is your security policy configuration.
Why the public key is not the problem
Supabase's anon key and Firebase's web config are designed to be public. They identify your project, not authorise access. Security depends entirely on:
- Supabase: Postgres row-level security (RLS) policies.
- Firebase: Firestore, Realtime Database and Storage security rules.
If those are missing or permissive, anyone can use the public key to read or modify your data, without using your app at all.
Common Supabase mistakes
- RLS disabled on a table. With RLS off, the anon role may read or write all rows, depending on grants.
- Policies that are too broad, such as
using (true)on select. - Insert or update policies without
with check, letting users write rows for other users or set their own role. - Relying on the frontend to filter by
user_id, without a policy enforcing it. - Service role key exposed in frontend code, which bypasses RLS entirely.
- Views and functions with
security definerthat bypass RLS unintentionally. - Storage buckets set public or missing storage policies.
A safe pattern:
alter table notes enable row level security;
create policy "owners read" on notes for select
using (auth.uid() = user_id);
create policy "owners write" on notes for insert
with check (auth.uid() = user_id);
Common Firebase mistakes
- Test mode rules left in production, allowing all reads and writes, or expiring and breaking the app.
- Rules that only check authentication, such as
allow read, write: if request.auth != null;, letting any signed-in user access everyone's data. - No validation of written data, so users can set fields like
role: "admin". - Storage rules allowing any authenticated user to read all files.
A safer pattern:
match /users/{userId}/notes/{noteId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
Multi-tenant apps
For B2B apps, policies must check organisation membership, typically through a membership table, not just user identity. Test that members of one organisation cannot read another's rows.
Testing your rules
- Use the public key directly, outside your app, and try to read every table or collection.
- Sign up as a new user and attempt to read and modify other users' data.
- Try to write privileged fields, such as roles or plan tiers.
- Check storage for listable or readable files.
- Write automated tests with the emulators or test databases to assert policies.
Checklist
| Check | Supabase | Firebase |
|---|---|---|
| Every table or collection protected | RLS enabled everywhere | No open rules |
| Ownership enforced | auth.uid() in policies | request.auth.uid matched |
| Writes validated | with check clauses | Field validation in rules |
| Admin keys server-side only | Service role never in client | Admin SDK only on server |
| Storage protected | Storage policies | Storage rules |
Automated testing
SecurityInfinity detects Supabase and Firebase backends used by your app, tests them with the public configuration and multiple accounts, and reports any data that is readable or writable when it should not be. Start free.
Frequently asked questions
Is it safe to expose the Supabase anon key?
Yes, it is designed to be public, provided RLS is enabled with correct policies on every table.
What happens if I leak the service role key?
It bypasses RLS and grants full access. Rotate it immediately and review database activity.
Are Firebase test mode rules safe for launch?
No. They allow open access and must be replaced with proper rules before going live.