A stranger on the internet can usually read more of your app than the screen suggests, because the screen was never the security boundary. If you shipped something real with an AI app builder or an AI coding tool, and you are not a security person, vibe coded app security comes down to one question you can answer for yourself: what does your server hand back to a request that never went through your interface? This post is the map: the five failure shapes that keep showing up, each in plain language, with a deeper post for every one.
What can a stranger read in your app right now?
Whatever your backend is willing to return to a request that skipped the interface entirely. Your app is really two things: a frontend that draws screens in someone’s browser, and a backend that stores and serves data. Only the second one is yours to trust. The first is a copy of your code running on a machine you do not control, where anyone can copy a request, change it, and send it again without the page involved.
Take an invented example, Acme Invoices. Users sign in, see their invoices, and pay for exports. The screens are correct and every button behaves, but none of that tells you what the backend does when a request arrives with no session, or with somebody else’s session. That gap is where nearly every finding here lives.
Why does vibe coded app security break in the same five places?
Because AI builders are very good at making the happy path work and have no way to know the rule you never stated. A generated app faithfully implements “show this user their invoices.” It cannot infer “and refuse everyone else,” because that rule lives in your head, not in your prompt. Five shapes follow.
1. The database is exposed to the browser with weak row rules
Many AI built apps talk to a hosted database directly from the browser using a public key, which is by design. The safety in that design comes entirely from row level rules that decide which rows each caller may see. If those rules are missing, left permissive, or written for one table and forgotten on the next, the browser can ask the database for everything and get it. Nothing is broken. The database is answering exactly the question it was asked. See Supabase RLS misconfiguration and Firebase security rules misconfiguration for how these policies fail in detail on the two most common hosted backends.
2. The paywall is enforced only in the browser
If Acme Invoices decides who gets exports by checking user.plan === "pro" in frontend code and hiding a button, the plan check is advice, not enforcement. The export endpoint still exists and still answers. Entitlements have to be decided on the server, from data the user cannot edit. We cover this shape in client side paywall bypass.
3. Secrets are shipped inside the frontend bundle
Anything your JavaScript can read, your users can read. A service key, admin token, or payment secret pasted into frontend code ends up in a file the browser downloads. Environment variables do not save you: if a build tool inlines the value into client code, it is public. Any key that has already shipped to a browser should be rotated, not hidden. See hardcoded API keys in frontend for which keys are safe on the client and which are not, and exposed .env file for the same secrets leaking through the server side instead.
4. The endpoint the interface never calls
Generated backends often include more routes than the app uses. A leftover admin route, a bulk export written during a refactor, a delete handler with no screen behind it. Nobody clicked it, so nobody tested it, and it inherits whatever protection the generator gave it by default, which is often none. This is the classic shape described in broken function level authorization, and it also shows up when file paths reach the server unvalidated, as in path traversal. In a Next.js app the same gap opens when a Server Action runs a privileged mutation with no check of its own, which we cover in Next.js Server Actions security.
5. Identifiers that can be changed to reach someone else’s data
If /api/invoices/1042 returns invoice 1042 to whoever asks, the number is the only thing standing between accounts. Sequential identifiers make this easy to notice, but random ones do not fix it either, since identifiers leak through shared links and exports. The server has to check ownership every time. Start with broken object level authorization, then read broken object property level authorization for the version where the object is yours but a field inside it is not.
Your interface decides what a person sees. Your server decides what a person can get. Only one of those is a security control.
Is there public evidence that this is common?
Yes, and the published work is worth reading as third party research, not a reason to panic. A public scan of the Lovable project gallery reported that roughly 170 of 1,645 applications exposed endpoints through missing or inadequate row level security. Imperva published findings on critical flaws in the Base44 AI app builder, including authentication bypass and exposure of sensitive data. A separate review of more than 1,400 production apps built this way found a majority carried security issues, many rated critical.
None of it means these tools are unsafe, and we have not tested any named product ourselves. It means a fast builder ships a working app, not a locked one, and the locking step is still yours.
What can you check on your own app in ten minutes?
Four checks, all read only. Only ever test applications you own or have written permission to test.
- Open it logged out. Use a private window, then request a data URL directly instead of clicking through screens. If a page or an API path returns real records with no session, that is your answer.
- Watch the network tab. Open a screen and read what the responses contain. If the interface shows three fields but the response carries email addresses or a plan flag, the hiding is happening in the browser.
- Search your bundle. Load your site, save the JavaScript, and search for strings like
secret,service_role,api_key, andsk_. Anything that looks like a credential is one, and it is already out. - Change one identifier. With two test accounts you created yourself, take a request from account A and replay it with account B’s session. If account B gets A’s data, you have found shape five.
Where a check fails, the fix is the same: move the decision to the server, tie it to the authenticated identity, and apply it on every route, not every screen.
Why do automated scanners miss most of this?
Because these are access control and business logic failures, and the rule that was supposed to exist is specific to your application. A signature scanner looks for known bad patterns: a string that reaches a shell, a library with a published advisory. It has no opinion about whether invoice 1042 belongs to the person asking, because nothing in the request looks wrong. The request is well formed, the response is a valid success, and the only thing missing is a rule nobody wrote down. That is also why these bugs survive review, and it is the wider category we cover in business logic vulnerabilities.
Finding them means understanding what your app is meant to do and then testing whether the server agrees, which is exactly what UnboundCompute is built for. More on that approach on our about page, or work through the rest of this cluster from the blog.
Frequently asked questions
What is vibe coded app security?
It is the practice of checking what a working application built with an AI app builder or AI coding tool actually exposes to the internet. These tools reliably produce a correct happy path, but they cannot infer the access rules you never stated, so the gaps show up in who the server will answer rather than in what the screens display.
What are the most common flaws in apps built with AI tools?
Five shapes recur. A database exposed to the browser with weak or missing row level rules, premium gating enforced only in frontend code, API keys shipped inside the JavaScript bundle, backend routes that exist but that the interface never calls, and object identifiers that can be changed to reach another user’s records. All five are access control failures rather than code injection.
How can I check my own app without being a security expert?
Open your app in a private window while logged out and request a data URL directly. Watch the network tab for fields the interface hides. Search your JavaScript files for strings that look like credentials. Then create two test accounts and replay one account’s request with the other’s session. Only run these checks against an app you own or have permission to test.
Why do vulnerability scanners miss these problems?
Because scanners match known bad patterns, and none of these requests look wrong. The request is well formed and the response is a valid success. The missing piece is a rule specific to your application, such as whether this invoice belongs to the person asking, and no signature list can know that rule for you.
Put an autonomous researcher on your own systems
UnboundCompute is an autonomous security researcher that reasons about how an application fits together and proves the access control and injection bugs it finds. We are opening a small number of founding design partner seats: private early access pointed at a staging target you choose, and a say in what it looks for. If your team ships software worth pressure testing, apply to the design partner program.
