The security holes in AI-built apps
The access-control flaws, exposed secrets and misconfigurations I find most often when reviewing apps built on Lovable, Base44 and Replit, with real examples of what they exposed.
Jun 30, 2026 · 9 min read
Field Notes from Thunkle, a studio that takes AI-built apps from prototype to secure, production-ready software.
A lot of my work is security: reviewing apps people have built on tools like Lovable, Base44, and Replit, finding what is exposed, and fixing it. These are the problems I see most often.
Most AI-built apps I review have at least one serious access-control flaw. The owners are rarely aware of it. The app works, so there is no reason to think anything is wrong.
That is what makes these flaws dangerous. The part of an app you can see, the interface, the sign-up, the checkout, is the part these tools build well. The part you cannot see, the rules for who is allowed to read and write each piece of data, is where they fall short. A missing rule does not cause an error. It produces an app that works and is exposed at the same time.
What follows is the set of issues I find most often, with examples of what they exposed. Every example is real and anonymized to its sector.
Unprotected databases
Most of these apps store their data in a backend service like Supabase or Firebase. These services are secure once configured, but not by default. They expect you to define who can read and write each table, what Supabase calls row-level security and Firebase calls security rules. When an app is generated and that step is skipped, the public API key, which is meant to be shared, gives access to everything behind it. This is the broadest form of broken access control, and it is the category I see most.
What this exposes, in practice:
A lending product had a table of full bank account numbers readable by anyone holding the public key. A health app exposed patient access codes and payment codes the same way. An education platform exposed the stored Google authentication tokens of its users, which is enough to access their accounts elsewhere. In each case the data was private in intent and public in fact.
Reading is the milder version. In several apps the tables could also be written to by unauthenticated strangers. I have seen this on tables named orders, payments, and transactions, where an outsider could insert a forged record the app would treat as real. In one app a table named security_events, the audit log meant to record suspicious activity, was itself writable by anyone, so an attacker could write over the record of their own actions.
The same misconfiguration extends to database functions. I have found privileged functions left callable with the public key, including ones named generate_license_key and generate_transfer_token. A stranger could call these directly and have the database mint a valid license or transfer token for them.
Firebase fails in a blunter way. Several apps were running in test mode, the temporary wide-open setting Firebase ships for local development, except they were live in production. One app's real-time database was readable from the root down, exposing top-level sections named users and wallets. Anyone who found the address could read it. Another left its storage open to anonymous uploads, so a stranger could put files of their choosing into the app's own storage, which invites everything from hosting malware to running up storage costs.
None of this is visible from the outside. The application behaves normally. The exposure is only apparent to someone using the public key the way an attacker would.
Reading another user's data (IDOR)
I find this one less often than the open databases above, but its impact is among the most serious, which is why it deserves close attention. The formal name is insecure direct object reference, or IDOR. It is a specific failure of object-level access control.
The app checks that you are signed in. It does not check that the record you asked for belongs to you. Records are usually addressed by an identifier in the request, often a sequential number. You sign in as yourself, load your own record at an address ending in /1024, change it to /1025, and the app returns the next user's record. Because the IDs increment, you do not have to guess. You count. Everything the app addresses this way is reachable: profiles, orders, accounts, documents.
I have found this in applications where the data is highly sensitive. A platform holding bond investments, where one account could read another's holdings by incrementing the ID. A trading application. A law firm's client portal. In each case the underlying fix was minor and the exposure was complete.
What makes this difficult is that it is invisible to the person who built the app. When they sign in and view their own data, everything is correct. The flaw exists in the relationship between two accounts, and it is only found by holding two accounts and comparing them. An owner testing their own app will not see it, and neither will most automated checks. This is why an application that looks entirely normal can still expose every customer's data to every other customer.
Identifiers leaking through public features
Not every exposure comes from a missing lock. Some come from a feature that is meant to be public but gives away more than it should.
A leaderboard, a public profile page, a list of recent activity, a reviews feed: these are built to be seen, so no one thinks of them as a security surface. But they are often assembled by returning a database record straight to the page, and that record carries fields the page never displays. I regularly find internal user IDs, email addresses, and other personal data sitting in the data behind a public feature, unused by the visible page but readable by anyone who looks at the response.
On its own that is a privacy problem. The larger issue is what those identifiers unlock. A public leaderboard that exposes each user's internal ID hands an attacker the exact set of IDs needed for the flaw above. The leaderboard tells you which numbers are real, and the missing object-level check lets you use them to pull each user's private record. Two issues that look minor in isolation combine into full account enumeration. Neither would stand out to the person who built the app, because both features are working as intended.
Exposed secrets
Some values are meant to stay on the server, where users cannot reach them. These include API keys, service credentials, and database connection details. To make an integration work, they are sometimes placed in the code that runs in the browser, where they can be read by anyone who opens the developer tools.
I have found a database's service key, the most privileged credential it has, exposed this way; it grants full access regardless of any other protection in place. I have found keys for paid AI services left in client-side code, in one app several of them bundled together. Those keys often have no spending limit, so a third party can use them at the owner's expense until the bill arrives.
The more serious versions remove every other obstacle at once. I have found hardcoded admin login credentials sitting in client-side code, which is a direct route to an administrator account. I have found database connection strings carrying root access, which is full control of the database with nothing else in the way. A credential that has appeared in the browser should be treated as already compromised, because every visitor can read it.
Access from any website
There is a setting, called CORS (cross-origin resource sharing), that controls which other websites are allowed to make requests to your app on a signed-in user's behalf. Configured incorrectly, it lets any site do so.
I have tested apps by sending requests that claimed to come from a site called evil.com, and watched the app accept that origin and return authenticated data to it. In practice this means a logged-in user who visits a malicious page in another tab can have their session used against your app without clicking anything. It is quieter than an open database, but it turns an ordinary bad link into a compromised account.
Authentication and configuration
Two smaller issues appear consistently. The first is weak password handling: apps that accept very short or trivial passwords, and login endpoints with no rate limiting, which lets an attacker guess passwords rapidly without being blocked. Together these make accounts straightforward to break into.
The second is security misconfiguration. Standard protective headers that a web app is expected to send to the browser, such as a content security policy, are often absent. Individually these are low severity. Their absence across an entire app usually indicates that no security configuration was done at any point.
What the AI leaves behind
Occasionally the app reveals how it was made. In one app the AI assistant's full system prompt was sitting in the client-side code, readable by any visitor, beginning "You are [Assistant], a direct, raw assistant. No lectures, no warnings, no disclaimers." Others left behind the model's working files, exposed notes on migrations and configuration, shipped to production and never removed.
I also find administrative panels and debug endpoints left reachable without authentication, which depending on what they control can be serious in their own right.
These are not always the most severe findings, but they are the most telling. They show an app assembled by a tool that was never asked to clean up after itself, and they give anyone looking a head start on everything above.
What you can check yourself
If you have built an app this way and it has real users, several of these are worth checking directly.
Confirm that your database has access rules defined and enabled on every table, and that none of its functions are callable without authorization. Move any keys or credentials out of client-side code onto the server, and rotate anything that has already been exposed. Sign in as one user and try to read another user's records by changing the identifier in the request. Look at the raw data behind any public feature, such as a leaderboard or profile page, and check it is not returning IDs or personal fields the page never shows. Review your password requirements, add rate limiting to your authentication endpoints, and check which sites your app accepts requests from.
These steps resolve the most common issues. They will not tell you which of your app's own rules were implemented incorrectly, or whether a later change has reintroduced a problem you had already fixed. Finding a vulnerability and fixing it properly are separate tasks, and on AI-built apps the second is usually where most of the work is.
That is the work we do at Thunkle. We review an app for these issues, fix them, and make sure the fixes hold as the app changes. If you would like a security review of your app, you can request a quote or reach us at thunkle.ai.
Working on something like this?
If you're migrating, securing or building an AI-made app, Thunkle can help. Get a fixed quote in 24 hours.
