A database doesn't have to be hacked to become a security problem. Sometimes the application is working normally while the permissions underneath it are far more open than the developer intended.
That's the concern behind research from UpGuard, which identified 16,326 Supabase databases with readable tables after examining roughly 300,000 websites showing signs of Supabase usage. UpGuard says more than half of those databases had indicators of personally identifiable information, while smaller portions showed indicators of passwords or authentication tokens. The research was published on September 25, 2026.
The headline number is important, but the more useful question is how those databases became readable in the first place.
UpGuard's findings point to configuration problems, particularly missing or inadequate Row Level Security (RLS), alongside the way modern AI-assisted development tools can create databases and application code programmatically. Supabase's own documentation makes the underlying model clear: access through its Data API depends on both Postgres privileges and RLS policies.
That distinction matters because 16,326 does not mean 16,326 confirmed data breaches. UpGuard did not read every record in every database. Its broad analysis looked at table schemas and then investigated a smaller number of cases in greater depth. The research therefore identifies a large population of databases with readable tables and indicators of potentially sensitive information; it does not establish that every one of those databases exposed personal data.
For developers, the more useful question is straightforward: How can a Supabase application work exactly as expected while its database permissions are still exposing information?
What UpGuard Actually Found
UpGuard's research began with approximately 300,000 unique domains that showed signs of using Supabase. The company used data from BuiltWith and Google's Chrome UX Report dataset to identify websites and then searched their public JavaScript for Supabase-related information, including project addresses and API keys.
The researchers then tested the databases for a common users table. Depending on the configuration, a project could return no accessible data, reveal the name of another accessible table, or return actual database results.
That process produced the headline figure: 16,326 databases with readable tables.
According to UpGuard, more than half of those databases contained indicators of personal information. A smaller percentage showed fields associated with passwords or authentication tokens, while a very small number had plausible credit-card data indicators. The company says the broad analysis relied on schemas rather than downloading every record across the full population.
The individual cases it investigated more closely show why that distinction can still lead to serious consequences.
One Indian adult-content platform had a users table containing tens of thousands of individuals and additional private messages. A U.S. valet service database contained customer contact information, license-plate records and business-history data. An immigration and relocation service had thousands of user records, including hundreds of plaintext passwords in one case.
A readable table doesn't automatically tell you how severe the exposure is. The real impact depends on what the application stores and what the exposed role is allowed to do.
That is why the technical configuration matters more than the headline alone.
This Is Primarily a Configuration Problem
The research does not describe one universal flaw that suddenly made every Supabase project vulnerable.
Instead, it highlights what can happen when the database's access controls don't match the application's intended security model.
Supabase's current documentation says a table in the public schema is reachable through its Data API. Without appropriate Row Level Security and policies, a role with the necessary privileges can access rows through that API. Supabase also separates grants from RLS: grants determine whether a role can perform operations against a database object, while RLS policies determine which rows that role can access.
That creates two related but separate security questions.
The first is whether a role can reach a table and perform an operation at all.
The second is whether that role is allowed to see a particular row.
A developer can enable RLS and still leave broader database privileges than the application needs. A polished application interface also isn't enough to prove that the underlying database is protected; if the Data API permits an operation directly, someone doesn't necessarily have to use the application's intended interface to perform it.
Supabase's documentation also distinguishes between how tables are created. Tables created through the Dashboard have RLS enabled by default, while developers creating tables through SQL or other tools need to enable it themselves. UpGuard connects this difference to programmatically created databases and AI coding agents.
That creates an easy-to-miss failure point.
A database can be functional. Authentication can work. The frontend can look finished. The application can even pass a basic manual test. Yet the underlying authorization rules can still be too permissive.
Why AI-Built Apps Are Part of the Story
AI coding tools have made it much faster to move from an idea to a working application.
An AI agent can generate frontend code, create tables, connect authentication and write the queries needed to make the application function. That's useful when speed matters, but it can also blur an important boundary: working software is not necessarily secure software.
UpGuard argues that the issue deserves particular attention because Supabase is widely used by AI-assisted applications, including projects where coding agents interact with databases programmatically.
The problem isn't that AI-generated code is automatically insecure.
The harder problem is that authorization rules are often specific to the application's business logic.
Imagine a project where each user has a private profile and a set of saved records. An AI-assisted workflow can successfully create the account system, the database tables and the pages that display those records. But another rule still has to be enforced: one user should not be able to request another user's rows simply by changing an identifier in an API request.
That rule belongs in the access-control model.
With Supabase, that usually means getting database permissions and RLS policies to match the application's actual rules.
A human developer can miss the same issue without AI. AI-assisted development can simply make it easier to create a large amount of functioning software before someone reviews every authorization boundary.
That distinction is especially important for smaller projects, prototypes and startups where one person may be responsible for the frontend, backend, database and deployment.
How Supabase Access Control Actually Works
Three concepts are central to understanding the issue: API keys, Postgres grants and Row Level Security.
API keys identify the client — they don't automatically make the data public
A Supabase application needs a way to identify the project it is connecting to. That means a key can appear in client-side code without automatically being a secret that must be hidden.
Supabase distinguishes between a publishable key intended for public client environments and an elevated secret key. A publishable key can be exposed when the database is protected by appropriate RLS policies and permissions.
So finding a publishable or legacy anon key in frontend JavaScript is not, by itself, proof that the database is exposed.
The important question is what that role is permitted to do.
Grants decide what a role can do
Postgres grants answer a basic question:
Can this role perform this operation against this table or database object?
A role with broader privileges than necessary may be able to read or modify data that the application never intended to expose.
That's why checking only the presence of an RLS policy isn't enough.
RLS decides which rows are accessible
Row Level Security adds another layer.
It answers:
Which rows is this role allowed to access?
For example, a user might be authenticated successfully and still need to be restricted to rows where the stored user identifier matches that account.
Supabase recommends using grants and RLS together. Its documentation also warns that adding an RLS policy doesn't automatically remove existing table privileges.
That leaves developers with a simple but important review requirement: check the permission model as a whole, not just whether a security feature is enabled.
Authentication and authorization are different
This is another distinction worth keeping clear.
Authentication answers, "Who is this user?"
Authorization answers, "What is this user allowed to access?"
A Supabase application can get authentication right and authorization wrong.
A person might log in successfully, receive the correct session and see their own dashboard, while the underlying API still allows access to records belonging to someone else.
The login screen can't tell you whether the database policy is correct.
The database policy has to do that work.
The Public API Key Is Not the Vulnerability
This is one of the easiest details to misunderstand.
Supabase applications are designed to make client-side requests, so a browser or mobile application needs a way to identify the Supabase project it is connecting to. Supabase explicitly separates the public client credential model from its elevated server-side credential model.
That means seeing a publishable or legacy anon key in application JavaScript isn't enough to conclude that sensitive data is exposed.
The problem starts when the public-facing role has access to information that should require authorization.
Put simply, the key can be public while the data still needs to be private.
The elevated secret key is different. Supabase says secret keys replace the legacy service_role key, bypass RLS and must remain inside trusted server-side environments. They should not be exposed in browser code or other public clients.
That distinction is important because developers sometimes focus on whether a key is visible instead of asking what the key is actually authorized to access.
What Developers Should Check Now
The practical response to UpGuard's research isn't to abandon Supabase. It's to audit what each project actually exposes.
Start by reviewing every table and view reachable through the Data API. Classify them according to their intended access:
- public information that really is meant to be public
- information that requires an authenticated account
- user-specific data that should be limited to the owner's rows
- data that should not be reachable through the client-facing API at all
For tables containing user-specific or otherwise sensitive information, RLS should be enabled and the policies should match the application's actual authorization rules. Supabase recommends enabling RLS for tables and views exposed through the Data API and using least-privilege access.
Then review the grants.
A policy can't compensate for a privilege model that is broader than necessary. Check what anon and authenticated are actually allowed to read, insert, update or delete. Supabase's documentation treats grants and RLS as complementary controls rather than substitutes for each other.
Next, check where credentials live.
Publishable keys are intended for client applications. Secret keys aren't. If a secret or legacy service_role key has been placed in frontend code or another public location, simply hiding it behind a UI change doesn't solve the problem; Supabase says such credentials bypass RLS and must remain under developer control.
Finally, test the database from the same privilege levels that an unauthorized user could obtain.
A table can appear safe through the normal application interface while still being directly reachable through the Data API if the underlying permissions are too broad.
A useful review therefore isn't just "Can I sign in?"
It is closer to:
- Can an unauthenticated request reach this table?
- Can an authenticated user access another user's rows?
- Can the client perform writes it shouldn't be able to perform?
- Are any elevated credentials exposed to client code?
- Do the database policies actually match the application's business rules?
The exact tests depend on the application, but the principle is consistent: test the authorization boundary itself, not just the interface built on top of it.
What the 16,326 Figure Does — and Does Not — Tell Us
The headline number needs context.
UpGuard did not say that all 16,326 databases contained confirmed sensitive information. It identified readable tables and then used schema information to identify likely categories of data. More than half had indicators of PII, but the company did not inspect every row across the full population. It investigated selected cases in greater depth.
That difference is important.
A database containing a publicly readable table of non-sensitive information isn't equivalent to one exposing passwords, private messages or identity records.
At the same time, the scale matters because a configuration mistake that might look like an isolated developer error becomes a broader industry concern when the same type of exposure appears across thousands of independently built applications.
The most important number, then, isn't only 16,326.
It's the repeated pattern behind that number.
If the same class of configuration mistake can occur across many applications, developers have to look beyond individual bugs and examine the development workflow that produced them.
The Real Risk Is Hidden Authorization Debt
Traditional development processes often give security review a defined place in the workflow. AI-assisted development can compress the path from idea to production, which makes it easier for authorization work to lag behind the rest of the application.
One way to think about that problem is authorization debt.
The application has authentication, pages, forms and database operations. Everything appears to work. But the detailed rules governing who can access which records haven't received the same attention.
Supabase isn't unique in facing that challenge.
Cloud storage, APIs, managed databases and other hosted services have all shown that secure infrastructure doesn't automatically create a secure application. The customer's configuration still determines what the application exposes.
UpGuard puts the Supabase findings in that broader pattern, drawing comparisons with earlier waves of cloud misconfiguration involving services such as Amazon S3 and GitHub.
The newer wrinkle is speed.
An AI coding agent can create an application's frontend, database structure and data access logic much faster than a traditional workflow might. That productivity is useful, but it doesn't remove the need to inspect the security model.
In a smaller project, that review may fall to the same developer who asked the AI agent to build the application in the first place.
That's where the risk can become easy to overlook: the software looks complete because the visible features are complete.
The authorization layer can still be unfinished.
What This Means for Supabase
The research also raises a practical question about where secure defaults should end and developer responsibility should begin.
Supabase has already changed some defaults. Its documentation says tables created through the Dashboard have RLS enabled automatically, while tables created through SQL or other tools still require developers to enable it themselves.
That helps, but it doesn't remove the underlying responsibility.
RLS still needs policies that match the application's actual users and business rules. A developer can enable RLS and write an overly permissive policy. Broader database grants can also remain in place even when an RLS policy exists.
So the important security control isn't one checkbox.
It's the combination of who can reach a table, which operations they can perform, which rows they can access and whether elevated credentials can escape into the client.
That distinction becomes particularly relevant for teams using SQL migrations, infrastructure-as-code or AI coding agents. The more automated the setup becomes, the more important it is to make security requirements explicit rather than assuming the tooling will infer them correctly.
The Real Lesson for AI-Assisted Development
The Supabase research exposes a weakness in a development workflow that's becoming increasingly common.
AI coding tools are good at turning requirements into functioning software. Access control is harder because it depends on rules that are often specific to the application.
A customer's profile may be visible to that customer but not another authenticated user. Internal records may be accessible to administrators but not ordinary accounts. A public leaderboard may intentionally be readable by everyone.
Those aren't simply coding details. They're decisions about access.
An AI agent can generate the SQL and application logic, but someone still needs to verify that the resulting permissions express the intended security model.
That is the more useful way to read the 16,326 figure.
The research doesn't show that Supabase is universally unsafe, and it doesn't show that every AI-built application is leaking data. It shows how a repeatable configuration mistake can scale when a widely used database platform is combined with rapid, AI-assisted application development.
For developers, the practical lesson is straightforward: a working AI-generated application isn't the same thing as a security-reviewed application.
The code may compile. The site may launch. Users may be able to sign in.
The database still has to enforce what each of those users is actually allowed to see.
0 Comments
Have a question, feedback, or something to add? Share your thoughts below.