Shypset

Supabase table readable without login

Your Supabase anon key is public. Whether it can read your users table depends only on Row Level Security. This check sends the same request a curious visitor could, and tells you which tables answer.

Verified Runs after you verify your domain. Verification is free on every plan.

What it is

A frontend that uses Supabase talks to the Data API at https://<project>.supabase.co/rest/v1/. Each request carries the anon key and, after sign-in, the user’s token. Postgres then applies RLS policies to decide which rows the request can see. If RLS is disabled on an exposed table, or a policy allows the anon role to read everything, the table is open to anyone who copies the key from your JavaScript.

What can happen

Anyone can run one curl command and download the table: emails, names, phone numbers, orders, private messages, whatever lives there. No password cracking, no exploit. Because it looks like normal API traffic, you will probably not notice it in your logs either. If the policy also allows writes, they can change or delete data.

How Shypset checks it

This is a Verified check. Sending requests to your backend goes beyond what a browser on your homepage does, so we run it only after you prove you own the domain. Here is exactly what it does:

  • Finds your Supabase URL and anon or publishable key in the page and bundle.
  • Collects table names your bundle references, plus those in the API schema if it is exposed. Up to 25 tables; we do not guess names.
  • Sends GET /rest/v1/<table>?select=*&limit=1 with the anon key, three at a time, with short timeouts.
  • Keeps the HTTP status, whether a row came back, the column count and up to 10 column names. The row itself is discarded and never stored.
  • Raises severity to critical when column names look like personal or secret data.

It uses only GET and only the public key. A service_role key is never used for probing. If the Supabase project looks like it belongs to a third-party service rather than to you, we report that it was found but not checked.

How to fix it

Enable RLS on the table with alter table public.<name> enable row level security; and add policies for the access you want, typically using (auth.uid() = user_id) for the authenticated role. Remove any using (true) policy for anon unless the data is meant to be public, like a pricing table, in which case you can ignore the finding. Then rescan: the anon key should no longer return rows.

Example finding

A typical finding and the fix prompt that comes with it.

criticalSupabase table orders is readable without login

Evidence

Table: orders
Request: GET /rest/v1/orders?select=*&limit=1 with the anon key
HTTP status: 200
Rows returned: yes
Column count: 9
Columns (first 5): id, user_id, email, address, total

Fix prompt

The Supabase table orders can be read with the anon key. Write a migration that enables RLS on public.orders, adds a select policy for the authenticated role using (auth.uid() = user_id), and no policy for anon. Check all other tables in the public schema the same way and list any that still allow anonymous reads.

Questions

Why do I need to verify my domain for this check?
The probe queries your backend directly. We only do that for sites whose owner has confirmed it, and verification is free on every plan.
Does this check find policies that leak data between signed-in users?
No. That needs a logged-in test, and Shypset does not log in. It finds tables open to anonymous visitors.

Check your Supabase tables

Scan your app from the home page, then verify the domain to run the table probe.

Scan your app