BlogVibecode

Why can my users see each other's data?

The screen shows the right things, so it looks fine. Until someone changes a number in the URL. What Row Level Security is, why it is off, and how to check it without a developer.

By Bas Voets7 min read

This is the problem I get called about most, and almost always after it has already gone wrong once. Somebody calls to say they can see another company's quote. Or worse: nobody calls, and you find out months later.

What is actually happening

Your app fetches data from a database. In most vibecode projects that is Supabase, and the fetching happens straight from the browser. That is a perfectly good design, but it means the database itself has to decide what someone is allowed to see. The browser cannot decide that, because the browser sits on your user's computer and they can change anything about it.

The mechanism that handles this is called Row Level Security: rules on the table itself, along the lines of “you may only see rows with your user id on them”. If that is on and the rules are right, a user requesting somebody else's data simply gets nothing back. If it is off, they get everything.

Why it is off in your project

Because it is in the way during the build. Supabase turns it on by default for a new table, and the first thing that happens then is that your screen stays empty. The fastest fix is to turn it off, or add a rule that allows everyone everything. The app works again, you build on, and nobody comes back to it.

This is not stupidity and it is not specific to AI tools. It is simply what happens when the thing you are protecting is invisible and the thing you are building is visible.

Checking it yourself, in ten minutes

  • Create a second account with a different email address, in your real app.
  • Log in with it and find a page with a number or code in the address: /quote/12, /project/abc.
  • Change that number to something belonging to your first account and press enter.
  • If you see data belonging to the other account, the shielding is only in the screen.

If that does not work because you have no numbers in your addresses, look in Supabase itself: the Table Editor shows per table whether RLS is on or off. Every table with customer data where it is off is a table anyone with a key from your browser can read out.

A screen that does not show something is not hiding it. It requests the data and decides not to draw it. By then it has already been sent.

What the fix involves

Working out per table who may see, create, change and delete which rows, and writing that down as a rule. That is thinking about your own process, not about technology: should an employee see the invoices of every client or only of their own files? Once those questions are answered, writing it down is a matter of hours.

What should follow is a test that proves it: a script that tries to reach B's data as user A and fails if it succeeds. Without a test like that you will be unsure again at the next change.

If it has already gone wrong

Close the door first and look at the rest afterwards. Rotate your keys, switch RLS on for the tables with personal data and accept that part of your app is temporarily empty. An app that shows too little can be fixed; leaked customer data cannot. And count on a real data breach having to be reported, that is not advice from me, that is the law.

Let's build

Tell me what you
want to build.

A half-hour call. You get an honest answer about what it costs, how long it takes and whether building it is smart at all.

Bas Voets · Webframer
0:00 / 0:00
Why can my users see each other's data?