All posts
Security

How to keep your vibe-coded app secure

The security holes in AI-built apps are the same few every time: keys the browser can read, permission checks that only exist in the interface, and a database with the doors left open. How to find each one yourself.

Eve Anderson

5 min read

Security tends to be the part people put off, on the grounds that you need to know what you’re doing before you can tell whether anything is wrong. In practice the problems I find in AI-built apps are drawn from the same short list, and you can check for most of them yourself with nothing more specialised than your browser’s developer tools. Finding out where you stand is the part you can do alone. Some of what you turn up will want an engineer.

Here they are in the order I would work through them.

1. Keys the browser can read

Your app holds keys for the services it talks to: the database, whichever AI API it calls, the email provider, the payment processor. Each one works for whoever is holding it, so a key that reaches the browser has effectively been published.

AI builders put keys wherever the app needs them to be, and the browser is often the convenient place. The distinction the tools blur is between a secret and an environment variable. An environment variable is any named value the app reads at runtime instead of having it typed into the code. Some are fine to expose, like a public analytics ID or a Supabase anon key that’s protected by row-level security. Some must never leave your server, like a Stripe secret key or a Supabase service_role key, which bypasses your access rules entirely by design. What decides which is where the value is read, not what it’s called. In most frameworks the browser-bound ones carry a prefix such as PUBLIC_, VITE_ or NEXT_PUBLIC_, so a real secret sitting behind one of those prefixes is going out to every visitor.

To check it: open your app, open developer tools, and search the page source and the network requests for the first few characters of each key you know the app uses. Anything you can find there, a visitor can find too. The fix is to move that call to your server so the key is used somewhere nobody can see it, and then to reissue the key, because you have to assume the old one is already in someone’s collection.

2. Permission checks that only exist in the interface

The second most common problem is an app that decides what you’re allowed to do in the browser rather than on the server.

It looks correct from the outside. The admin link only renders for admins, the delete button is hidden from ordinary users. But the browser is under the visitor’s control, and anyone can watch what the button would have sent and send it themselves with a single command. If the server doesn’t independently check who is asking before it acts, there’s no check.

To check it: log in as an ordinary user and go looking for things you shouldn’t be able to reach. Change the ID in a URL to point at another person’s record. Type in the path of an admin page rather than clicking through to it. Delete something that belongs to someone else. If any of that works, it works for everyone. The rule is that every action that matters gets re-checked on the server, every time, regardless of what the interface offered.

3. A database anyone can query

Many vibe-coded apps talk to their database more or less directly from the browser, which is convenient and, without the right settings, means the browser can ask it for anything.

On Supabase the setting that matters is row-level security. With it off, the anon key that ships to every visitor can read whole tables. With it on, you write policies that say which rows a signed-in user is allowed to see, and everything else is denied. You can build a complete, working app without ever enabling it, and nothing in the app will look wrong while you do.

To check it: open the Supabase dashboard, go to the table editor, and look for the “RLS disabled” warning on each table. Any table holding user data with that warning on it’s readable by anyone who opens your app and copies the anon key out of it. Turning RLS on will break parts of your app until the policies are right, which is why it’s worth doing deliberately rather than five minutes before launch.

4. The unglamorous rest

Three more worth going through once the first three are done:

  • Logins. If people sign in, confirm that password reset can’t be used to take over another account, that sessions expire, and that the reset tokens aren’t guessable. If you’re using an off-the-shelf provider like Supabase Auth or Clerk, the hard parts are handled and the edges are where mistakes live.
  • Old dependencies. Generated projects often pin packages that were already a year out of date. npm audit in the project folder will list the known holes, and most of them close with an update.
  • Rate limiting. Try submitting a form fifty times in a row, or getting a login wrong twenty times. If nothing stops you, nothing will stop the people who do this for a living either, and that’s how you end up with a spam problem or a bill.

Where to start

Don’t try to do all of this in one sitting. Start with the keys, because that’s the problem that’s invisible from the outside and can cost you money overnight. Then the permission checks, then the database. That order deals with the most expensive things first.

Building the app was the creative part, and you have done it. Working through a list like this afterwards is slower, fiddlier work, and it’s exactly the sort of thing you can hand to someone who does it all the time.

Keep reading

Security

Can one of your users see another user's data?

Broken access control is the most common serious flaw I find in AI-built apps. The interface hides things, but the server never re-checks who is asking - so user A can reach user B's data.

Eve Anderson

8 min read
Read
The opening slide of the AI under the hood talk, reading "AI under the hood: not magic, just unimaginable scale".
AI

How does AI work under the hood?

A talk I gave on what’s actually happening inside modern AI. No magic, just patterns learned from data at a scale that’s hard to picture, from a single Titanic passenger list to large language models.

Eve Anderson

1 min read
Read
Launch

The launch checklist for an AI-built app

Everything I check before an AI-built app meets real users: security, data protection, reliability, performance, scaling, deployment, code quality and accessibility, with the concrete points under each and how to prioritise them.

Eve Anderson

10 min read
Read