The One Key That Ignores All Your Security Rules
You can write perfect row-level security policies and still hand out every record in the database, because one kind of key is designed to bypass them. Here is how to tell your keys apart.
By NinjaNext TechnologiesPublished 4 min read
Key takeaways
- Most hosted databases issue two kinds of key: one safe for the browser, one that bypasses every rule you wrote.
- The dangerous one is meant for servers only. In an AI-built app it often ends up in the browser bundle.
- If a secret key has already shipped, moving it is not enough: it must be rotated, because the old one is public.
- Check the built bundle, not just the source. That is what your users actually download.
If you have read our piece on row-level security, you know how to stop one user reading another user's records. There is a loophole worth understanding straight after, because it makes all of that work irrelevant in a single line of code.
Hosted database platforms typically issue two kinds of key. One is designed to be public and sits safely in your browser code. The other is designed for servers, and it is built to bypass your access rules entirely, because server code sometimes genuinely needs to read or write anything. That is not a flaw. It is the point of that key.
The problem is that both are strings in a settings panel, they look much alike, and nothing stops you pasting either one into the wrong place.
Telling your keys apart
Naming varies by provider, but the pattern is consistent. The browser-safe key is usually described as public, anonymous or publishable. The dangerous one is usually described as a service role, a secret key, or an admin or server key. If a key's own documentation says it bypasses security rules, or warns you never to expose it in a client, that is the one.
When you are unsure, the safest assumption is the strict one: treat an unfamiliar key as a secret until you have confirmed otherwise in the provider's own dashboard. The cost of being wrong in that direction is an afternoon. The cost of being wrong in the other direction is your entire database.
How it ends up in the browser
Nobody decides to ship a secret key. It arrives by a few familiar routes. An AI builder is asked to make an admin screen work, and the quickest way to make a stubborn query succeed is a key that is never refused. A variable gets a name that marks it as public, because that is the prefix the framework requires to make it available in the browser, and the prefix is treated as a formality rather than a declaration. Or server-side code and client-side code live close together, and the import crosses the line without anyone noticing.
This is also why a key can be absent from your source files and still reach your users. Frameworks bundle environment variables into the files the browser downloads according to naming rules. The source is not the artefact. The bundle is.
How to check, in five minutes
- Search your whole codebase, not just the front-end folder, for the words service, secret, admin and private alongside your provider's name.
- Build the app the way you deploy it, then search the built output for the first and last few characters of each secret key. If it appears there, it is public.
- Open your deployed site, open the browser's developer tools, and search the loaded scripts for the same fragments. This is the definitive test: it is exactly what any visitor can do.
- Check which environment variables carry a public prefix. Anything with that prefix is in the browser by design, so a secret must never be given one.
Do not paste a key into a third-party site to check it. That is one more place it has been.
If one has already shipped
Moving the key to the server is not the fix, and this is the part people get wrong. Once a key has been served to browsers it has to be treated as public, whoever did or did not notice. It may sit in caches, in logs, in someone's saved copy of the page.
- Rotate the key in the provider's dashboard, so the exposed one stops working.
- Update the new key everywhere the server genuinely needs it, and nowhere else.
- Redeploy and repeat the bundle check above against the new build.
- Look at your provider's logs for unusual access while the old key was live. You may find nothing, which is worth knowing either way.
In the platforms we build for clients, secrets live in the deployment environment rather than in the code, and the applications run on the client's own hosting and accounts, so the people who own the product own the keys too. That arrangement makes rotation an ordinary task rather than an emergency.
What to do about it
Run the bundle check on your deployed site today. It takes five minutes and the answer is unambiguous. If a secret key is in there, rotate it before you do anything else, then fix the place it came from.
This and the row-level security question together account for most of the serious problems we find in AI-built apps, and they are both checks you can run yourself. If you would rather have the whole thing looked at properly, send us the app: we review existing code before changing any of it, and tell you what to keep, fix or rebuild with a fixed quote. If you are still scoping, tell us what you are building.