Shypset

Supabase security: is your data readable without login?

Supabase gives your frontend a direct line to Postgres. That is what makes it fast to build with, and it is also why one missing policy can open a whole table to the internet. Here is how the access model works, what goes wrong and how Shypset checks a live app.

How access works in a Supabase app

Your app ships two things to every browser: the project URL and the anon (or publishable) key. Both are public by design. With them, anyone can call the Data API at /rest/v1/<table>. What they get back is decided entirely by Row Level Security. If RLS is enabled and the policies are tight, the anon key can only do what you allowed. If RLS is off on a table that the Data API exposes, the anon key can read all of it.

Supabase has improved its defaults: the dashboard warns about tables without RLS, Security Advisor flags them, and since 30 October 2026 new tables are no longer exposed through the Data API automatically. Projects created before that, and tables an AI tool created with explicit grants, still need checking.

What usually goes wrong

  • RLS disabled on a table the app reads, often one created later in development.
  • A policy such as using (true) for the anon role, added to fix a permission error.
  • The service_role key in client code. It bypasses RLS completely, so it is equivalent to the database password.
  • Storage buckets marked public that hold user uploads.
  • Policies that check that a user is signed in but not that the row belongs to them.

The last one is the hardest: it needs a logged-in test to find, and Shypset does not log in. We catch the anonymous cases.

What Shypset checks on a Supabase app

Public, on every scan: we find the Supabase project URL in your page or bundle, decode any Supabase JWT we find and raise a critical finding if its role is service_role, and search the bundle for other secrets.

Verified, once you prove you own the domain: we collect the table names your bundle references, plus those listed in the API schema if it is exposed, up to 25 tables. For each one we send GET /rest/v1/<table>?select=*&limit=1 with your public anon key, the same request any visitor could make. We store the status, whether a row came back and up to 10 column names, never the row itself. Tables are not guessed: we only probe names your own app uses.

Shypset is a passive external check. It does not log in to your app and does not test application logic such as SQL injection or IDOR, so a clean report means nothing is visible from outside, not that the app is secure.

How to fix it

Run alter table <name> enable row level security; on every table in the exposed schemas, then add policies for the access you actually want, usually auth.uid() = user_id. If the service_role key leaked, rotate it in the dashboard under API settings and move any code that needed it into an Edge Function. If a table is meant to be public, such as a list of plans, you can keep a read policy for anon and ignore the finding.

Example finding

A Public finding about a leaked key, as shown in the report, with its fix prompt.

criticalSupabase Service Role Key Exposed in Client Code

Evidence

Service role JWT found in client bundle (masked): eyJhbGciOiJIUzI1NiIs…[masked]…Qm4

Fix prompt

The Supabase service_role key is in our frontend bundle. I have rotated it. Find every place the client uses it and replace it with the anon key. Any operation that truly needs elevated rights must move into a Supabase Edge Function that reads SUPABASE_SERVICE_ROLE_KEY from its secrets. Then make sure RLS is enabled on every table in the public schema.

Questions

Is it safe that my Supabase anon key is public?
Yes, if every exposed table has RLS with correct policies. The key itself is not a secret; the policies are the security.
Does Shypset read my data?
The probe asks for one row to see whether access is open, then discards it. No row data is stored or shown.

Check your Supabase app

Scan the site that uses Supabase, then verify your domain to run the table probe.

Scan your Supabase app