Get your Supabase RLS reviewed before user data leaks through the wrong policies
Row Level Security decides who is allowed to see which row. A single overly broad policy is enough for users to read other users' data. As a Supabase security audit, Veriploy reviews your Supabase RLS across tables, auth, storage and edge functions and keeps it under ongoing technical oversight afterwards, instead of stopping at a one-off look.
- Baseline check 490 €
- Policies + auth + storage
- Reproducible tests
- German point of contact
Technical point of contact
Timo Wevelsiep
Software engineer, cloud architect, founder & managing director
I review code, security and infrastructure and surface what is technically risky before launch, customer use or due diligence.
For questions like:
- Is this release ready for production?
- Which CVEs are really critical?
- Will the architecture carry the next users?
What Row Level Security in Supabase is
Row Level Security (RLS) is access control right inside the Postgres database. Instead of deciding in the frontend or the API who sees what, RLS policies define per table and per operation which rows a user may read or write. This is how it decides data separation:
Why authentication is not the same as authorization
Authentication answers the question of who someone is. Authorization answers the question of what that person may see and change. Many Supabase projects solve the first part cleanly through Supabase Auth and assume the second part is handled too. It is not.
A valid login only means a request carries a real user. Whether that user can then reach another tenant's invoices, messages or files is decided solely by the RLS policy. If it is missing or too broadly written, every logged-in user, and in the worst case every anonymous request, sees more than intended.
That is exactly where our review starts: we check not only whether auth works, but whether authorization really holds per row and per operation, down to the policy condition itself.
Common RLS mistakes we find
RLS is powerful but easy to misconfigure. Tables created in raw SQL or via the SQL editor have RLS off by default, while tables created with the Table Editor have it on. These are the gaps we see most often:
| Mistake | What it leads to |
|---|---|
| RLS not enabled | The table is fully readable via the anon key, without any login. |
| Overly broad select policies | Conditions like true or a missing user_id check expose every row. |
| Missing insert/update/delete limits | Reads are protected, but users can write to foreign or arbitrary records. |
| Public storage buckets | Files like uploads or receipts are retrievable without a storage policy. |
| service_role key in the wrong context | The key bypasses RLS entirely and ends up in the frontend or is used in edge functions without their own auth and input checks. |
| Tenant ID only checked in the frontend | Tenant separation is easily bypassed through manipulated requests. |
What Veriploy reviews in the RLS check
We walk the access chain systematically, from the table to the edge function, and rank every finding by severity. We review:
- Tables: is RLS enabled everywhere user data lives
- Policies: are select, insert, update and delete clean and not too broad
- Auth: do auth.uid() and JWT claims behave as expected per role
- Storage: are buckets and file policies protected against anonymous access
- Edge functions: is the service_role key used only server-side and under control
- Tests: reproducible checks that deliberately attempt anonymous and cross-tenant access
A concrete pattern: CVE-2025-48757
How real incomplete RLS gets is shown by CVE-2025-48757. In May 2025 a security researcher documented that more than a hundred apps built with a low-code tool had Supabase tables readable, and partly writable, via the public anon key without any login. The vulnerability was rated critical per the CVE record (CVSS 9.3), in the Incorrect Authorization category.
An important note for context: this was not a bug in Supabase itself. The cause sat in the configuration of the generated apps, missing or overly broad RLS policies on tables whose RLS was off by default. Supabase provides RLS, but whether it correctly holds per table and operation is decided by the individual app.
This exact pattern, automatically generated code plus assumed but unverified data separation, is why we do not tick RLS off once but keep it under ongoing oversight. Every new table and every changed policy can soften the separation again.
What a finding looks like
RLS for the messages table is not enabled, contents are readable via the anon key without any login. Recommendation: enable RLS and enforce a select policy on auth.uid() = user_id.
One-off look at the RLS or ongoing oversight?
| One-off look | Veriploy ongoing | |
|---|---|---|
| Timing | Point-in-time snapshot on a fixed date | Continuous, with every new table and policy |
| New tables | Not covered, RLS often stays off | New tables without active RLS surface early |
| Policy changes | Not re-checked | Overly broad changes are flagged |
| Storage and edge functions | Rarely included | Buckets and service_role usage in view |
| Assessment | A list at the end | Human prioritisation, not just a score |
Frequently asked questions
Is it not enough that Supabase Auth is active?
No. Auth only clarifies who is logged in. Which rows that user may read or change is decided by the RLS policy. Without active and correctly limited policies, a logged-in or even anonymous request can see more than intended. We check exactly this authorization per table and operation.
Do you also check storage buckets and edge functions?
Yes. The RLS review does not stop at the tables. We check whether storage buckets and file policies are protected against anonymous access and whether the service_role key is really used only server-side and under control, not in the frontend or unprotected in edge functions.
Do you fix the policies yourselves?
Not within the plan. We review, prioritise and explain which policy is missing or too broad and how it should look. Implementation runs through your team or separately through Wevelsiep Advisory or WZ-IT. That keeps the review independent from the implementation.
What do you need for the review?
Read access to the repository and insight into the database schema including its policies. We do not need write access to the production database. For the reproducible tests, a test or staging environment is usually enough.
What does a Supabase RLS review cost?
The entry point is fixed: Snapshot 249 € and Baseline 490 € as one-off reviews, the RLS review is part of the Baseline check. Ongoing oversight starts at 299 € per month (Watch), then Guard at 749 € and Launch at 1.490 € per month. All prices are net plus VAT, plans cancellable monthly.
What does CVE-2025-48757 have to do with this?
This case shows how real incomplete RLS gets: more than a hundred low-code apps had tables readable via the anon key without any login, rated critical per the CVE record (CVSS 9.3). The cause was misconfiguration, not a Supabase bug. This is exactly the pattern we review and keep under ongoing oversight.
- Vibe coding security audit, plus ongoing control as the code keeps growing
- Get your Lovable app reviewed before Supabase, RLS or secrets become a risk
- Infrastructure audit for AI-built software, review your deployment, backups, monitoring and secrets
- Get your AI app reviewed, with ongoing technical oversight instead of a one-off gut check
Supabase RLS review in the Baseline check, before data leaks.
Start with the Baseline check and keep policies, storage and edge functions under ongoing oversight afterwards.