
🤖 Ghostwritten by GPT 5.4 · Fact-checked & edited by Claude Opus 4.6
A public Supabase anon key or Firebase client configuration is not automatically a security mistake. The real mistake is exposing a public key without row level security — or without properly written Firebase Security Rules — because that turns a normal browser app into an open door to the database. That is the practical lesson from the RedAccess "Shadow Builders" findings disclosed in May 2026: according to the report, roughly 5,000 of about 380,000 scanned vibe-coded apps had no authentication at all, and about 40% of those no-auth apps were actively leaking sensitive data through exposed Supabase and Firebase access patterns.
For vibe-coders, the important nuance is simple: the key in the front end is supposed to be there. The lock is supposed to be in the database. Row level security, often shortened to RLS, is that lock. It tells the database which rows a user can read, insert, update, or delete. If those rules are missing, the database may hand over everything to anyone who knows the public key and API endpoint.
TL;DR: A public anon key or Firebase client key is normal; the danger starts when database authorization is missing behind it.
Many new builders panic the first time they inspect their own front-end bundle and see a Supabase URL plus an anon key, or a Firebase config object in plain text. That reaction is understandable, but it misses how these platforms are designed to work.
In Supabase, the anon key is a client-side credential for an untrusted environment: the browser or mobile app. In Firebase, the web app config is also designed to live in the client. These values identify the project and allow the app to talk to backend services. They are not the final gatekeeper for sensitive data.
The real gatekeeper is database authorization:
This distinction matters because many vibe-coders learned a simplified rule that says "never expose keys in the browser." That rule is useful for secret server keys, but it is incomplete for platforms built around public client credentials.
The RedAccess findings underscore this: the sharper lesson is not "public keys are always bad." It is "public keys are only safe when the database enforces per-user access."
| Scenario | Public key in app | RLS / Security Rules | Result |
|---|---|---|---|
| Proper setup | Yes | Yes | Users can access only allowed data |
| Dangerous setup | Yes | No | Anyone may read or write broad database contents |
| Server-secret leak | No public key involved | Irrelevant | High-risk secret exposure of a different kind |
For a vibe-coder, this is the foundational mental model: a public key is an address label, not a deadbolt. The deadbolt is RLS or Security Rules.
TL;DR: Row level security means the database checks each row and asks, "Is this specific user allowed to touch this specific record?"
The term row level security sounds more complex than it is. Think of a table called notes with columns like id, user_id, title, and body. Without RLS, a query from the front end might return every note in the table. With RLS, the database can be told: only return rows where user_id matches the currently signed-in user.
That turns a broad database into a set of private slices.
A plain-English version of a typical rule looks like this:
user_id equals their own account IDuser_id equals their own account IDThat is why RLS is the lock behind public keys. Even if a stranger opens the app, copies the anon key, and sends requests directly to the backend, the database still evaluates the rule for every row.
In Supabase, RLS is implemented on PostgreSQL tables. Supabase's security model expects developers to enable RLS and define policies. If RLS is off, table access can be far broader than intended.
A starter policy pattern often looks like this conceptually:
| Action | Rule idea |
|---|---|
| Select | Allow when row user_id equals authenticated user ID |
| Insert | Allow when new row user_id equals authenticated user ID |
| Update | Allow when existing row user_id equals authenticated user ID |
| Delete | Allow when row user_id equals authenticated user ID |
Firebase uses a different mechanism, but the idea is the same. Firestore Security Rules and Realtime Database Rules decide whether a user can read or write documents or paths. The lock is still in the data layer, not in the JavaScript UI.
That is why "hide the admin page" is not security. If the database would still return another user's records, the app is not secure.
TL;DR: AI-generated apps often look correct in single-user testing, so missing RLS stays invisible until a second person shows up.
This is where the RedAccess findings connect directly to everyday vibe-coding behavior. Many AI tools generate an app that signs in a user, stores data, and displays it back beautifully. In a solo test, everything appears to work.
That creates a false sense of safety for three reasons.
If only one account exists, a query that returns all rows still appears correct, because all rows belong to that one tester. Nothing looks broken.
Code generation tools are often rewarded by whether the app runs, not whether its authorization model is production-safe. A generated query that fetches an entire table can make the UI look complete faster than a carefully scoped access policy.
A generated app might hide certain screens unless a user is logged in. That can look like protection, but it is only a user interface condition. If the database itself has no row restrictions, a direct request can still pull the data.
This is why the most important test is not "does login work?" The load-bearing test is: what happens when user B tries to access user A's data?
The RedAccess reporting highlights exactly this gap between a convincing demo and a secure multi-user product. Thousands of vibe-coded apps exposed sensitive data through combinations of missing authentication, exposed backend details, and weak authorization controls. That pattern is not exotic. It is what happens when the app is built for the first user and never tested against the second.
TL;DR: Check every table, enable RLS or Security Rules everywhere they are needed, write owner-only access rules, and test with a second user.
For a vibe-coder, this does not need to become a month-long security project. The first pass is straightforward.
Open the Supabase or Firebase dashboard and review every data store your app uses.
Look for:
The dangerous assumption is that the visible user-data table is the only thing that matters. In practice, helper tables often get left open.
The safest default for many consumer and internal apps is simple: each user can access only their own rows.
That usually means every user-owned record should include a stable owner field such as user_id. Then the policy can compare that field against the authenticated user identity.
If a table is shared by teams, organizations, or roles, the rule can be expanded later. But owner-only access is the right baseline for many early apps.
This is the step many builders skip, and it is the one that catches the real problem.
Create two test users with different accounts. Then verify:
If User B can see User A's records, the app is not protected, even if the UI looks polished.
A hidden button is not authorization. A disabled form is not authorization. A route guard is not authorization. Those are interface choices. The actual security decision must happen in the database or rule engine.
This principle applies across Supabase, Firebase, and any modern backend-as-a-service stack.
TL;DR: Use AI to speed up the audit, but make it inspect every table and produce explicit owner-only starter policies instead of vague advice.
Here is a prompt a vibe-coder can paste into an AI coding assistant. Review the output carefully before applying anything to production.
Audit my app's database authorization. I am using Supabase or Firebase. List every table, collection, or path that stores user data. For each one, tell me whether row level security or Security Rules are enabled, what the current read/write exposure appears to be, and whether anonymous or authenticated users can access more data than intended. Then draft starter policies or Security Rules that restrict each user to only their own records using the owner field such as
user_id,uid, or equivalent. If a table does not have an owner field, flag it and suggest the safest schema change. Also give me a test plan using two user accounts so I can confirm user B cannot read, update, or delete user A's data. Do not assume the front end is a security boundary. Show the exact dashboard checks and the exact SQL policies or Firebase rules in a safe starter form with placeholders where needed.
If the assistant cannot determine current settings from the codebase alone, that is a useful result. It means the dashboard review still needs to happen manually.
Yes. The Supabase anon key is designed for use in the browser or mobile app. It is only safe when row level security and table policies are correctly configured to restrict what that client can actually access.
Yes. Firebase web app configuration values are normally embedded in client code. The security boundary is Firebase Security Rules, not secrecy of the client config.
Row level security is a set of database rules that checks whether a user is allowed to access a specific record. In many apps, the basic rule is: users can only read and change rows that belong to them.
Single-user testing often hides the problem. If only one person has data in the system, a query that returns everything can look correct until a second user signs in and sees records they should never have access to.
Create two accounts and add data under the first one. Then sign in as the second account and confirm that none of the first user's records can be read, edited, or deleted — including through direct API requests that use the public client key.
anon key or Firebase client config is usually normalThe May 2026 RedAccess reporting sharpened an important lesson for the current wave of vibe-coded software: the risky part is not that modern backend platforms use public client keys, but that many apps ship without the database rules that make those keys safe. As AI-assisted development keeps compressing build time, the difference between a convincing demo and a secure product will increasingly come down to one unglamorous discipline: verifying that the database says no to the wrong user, every time.
Discover more content: