Which Supabase key leaked decides everything. If your service_role, or secret, key is exposed, treat it as a full database breach: it bypasses Row Level Security and gives complete read and write access, so rotate it immediately and move it server-side. If your anon, or publishable, key is exposed, that alone is normal, because it is designed to be public. The real danger there is missing Row Level Security, which lets anyone query your tables directly with that public key, exactly what happened to over 170 Lovable apps in CVE-2025-48757. Bots find both automatically.
Short answer
The anon key is meant to be public; the service_role key is never safe to expose. Per Supabase's API keys documentation, the service_role, or secret, key bypasses Row Level Security and has full data access, so an exposed one is a critical breach: rotate it now and move it to a backend. An exposed anon key is only dangerous if RLS is off or wrong, because then anyone can query your tables with it, which is the root cause of CVE-2025-48757 in Lovable apps. Fix it by rotating any secret key, enabling and enforcing RLS on every table, and verifying that the public key returns nothing it should not.
Which Supabase key was exposed?
Your first job is to identify which key leaked, because the response differs completely. Supabase gives you an anon (publishable) key, which is low-privilege and meant to be shipped in the client, and a service_role (secret) key, which is a full-access admin key. There is also the JWT secret that signs tokens. The table below sorts them.
| Key | Safe to expose? | If exposed |
|---|---|---|
| anon / publishable | Yes, by design | Safe only if RLS is on and correct |
| service_role / secret | No, never | Full database access; rotate now |
| JWT secret / signing key | No, never | Attacker can forge auth tokens |
If it is the anon key, do not panic about the key itself; the question becomes whether RLS is protecting your data. If it is the service_role key or the JWT secret, treat it as an emergency, because those give an attacker administrative control that no RLS policy can restrain.
What attackers and bots can do with each
The service_role key is the worst case, because it bypasses RLS entirely. An attacker with it can read, modify, and delete every row in your database, regardless of any policy, and can often reach storage and admin functions too. There is no partial exposure; a leaked service_role key is a complete compromise of your backend.
An exposed anon key is different, and its danger depends on RLS. With RLS enabled and correct policies, the anon key can only do what your policies allow, so it being public is fine by design. With RLS off or misconfigured, the same public key lets anyone send direct queries and dump whole tables, user lists, payment records, or other secrets, without ever logging in. That second case is the common, devastating one in AI-generated apps.
The Lovable case: CVE-2025-48757
CVE-2025-48757 is the clearest real-world example of the anon-key-plus-missing-RLS failure. In May 2025, security researcher Matt Palmer disclosed that 303 endpoints across 170 Lovable projects, about 10 percent of the 1,645 he analyzed, had Supabase tables readable by unauthenticated requests using only the public anon key. Attackers could dump entire tables of sensitive data without any credentials.
The root cause was insufficient or missing RLS policies in the generated projects, made worse by the fact that Supabase creates tables with RLS off by default. The lesson is not that the anon key leaked, because it is supposed to be public; it is that nothing was protecting the data behind it. AI and low-code builders like Lovable make this easy to get wrong at scale, because they scaffold a database and a public key without always wiring up the policies that make it safe.
How bots find and exploit exposed keys
This is not a manual, targeted attack; it is automated and constant. Bots continuously scan public GitHub repositories, deployed JavaScript bundles, and app binaries for Supabase project URLs and keys, which follow recognizable patterns. When they find a service_role key, they test it against the REST API and gain full access in seconds. When they find an anon key, they probe your tables directly to see whether RLS is enforced.
Speed is the point. Because the scanning is continuous, a key committed to a public repo or shipped in a bundle can be found and tested within minutes, long before you notice. Supabase now automatically revokes secret keys it detects in public GitHub repositories and notifies the owner, which helps, but you cannot rely on catching it after the fact, because the exposure window is enough.
Immediate response: rotate the key
If a secret or service_role key is exposed, rotate it right away, because that is the only action that invalidates the copy an attacker may already have. In Supabase, how you rotate depends on which key model you use. With the newer API keys, you revoke the specific secret key and issue a new one, and Supabase can revoke leaked secret keys automatically. With the legacy keys, rotating the anon and service_role keys means rolling the JWT secret, which regenerates them and invalidates existing tokens and sessions, per Supabase's rotation guide.
Rotate first, then clean up. Remove the key from your client code and, critically, from your Git history, since deleting it in a later commit does not remove it from earlier ones. Treat any exposed secret key as compromised even if you rotate quickly, and review your logs for signs it was used before you rolled it.
The real fix: enable and enforce RLS
For the anon-key case, and as defense in depth for everything, Row Level Security is the actual fix. RLS is what makes a public anon key safe: with it enabled and correct policies written, the key can only reach data your policies permit. Supabase creates tables with RLS off, so you must turn it on for every table and write policies that match your authentication logic, such as letting users read only their own rows.
Do not treat RLS as optional or as something to add later. The entire security model of a Supabase client app assumes it, and CVE-2025-48757 is what happens when it is missing. Enable RLS on every table, write and test policies for each, and never leave a table world-readable on the assumption that the anon key is secret, because it is not.
Move the service_role key server-side
The service_role key should never be in your frontend, your repository, or your mobile app, because anything shipped to a client can be extracted. If you need admin-level operations, run them on a backend you control, such as a Supabase Edge Function or your own server, and keep the service_role key there as a server-side secret.
This separation is the architectural fix behind the incident response. The client uses the public anon key, protected by RLS; privileged work happens server-side with the secret key, which users never see. If an AI builder generated code that puts the service_role key in the client, that is a design flaw to correct, not just a key to rotate.
Verification checklist
After you respond, verify the fix rather than assuming it worked. The checklist below confirms each part.
| Step | Action | What it confirms |
|---|---|---|
| 1 | Rotate the exposed secret or service_role key | The old key is dead |
| 2 | Enable RLS on every table | No table is world-readable |
| 3 | Write policies that match your auth logic | Users see only their own data |
| 4 | Remove keys from the client and Git history | No re-leak of the old key |
| 5 | Query protected tables with the anon key | It returns nothing it should not |
| 6 | Review database and auth logs | No ongoing abuse |
The most important verification is the fifth step: actually query your tables with the public anon key and confirm it returns only what it should. That is the exact test an attacker or a scanning bot runs, so passing it yourself is the direct proof that CVE-2025-48757 style exposure is closed.
Scan before you ship
Because AI and low-code builders make these mistakes easy to ship, checking the build before it goes out catches them while they are cheap to fix. A committed service_role key, a missing RLS policy, or a secret in the bundle are exactly the patterns that automated attackers hunt for, so finding them first is what keeps you ahead of the bots.
A scanner like PTKD.com analyzes your mobile build and returns findings ordered by severity and mapped to OWASP MASVS, including embedded secrets and insecure network and storage patterns, so an exposed key does not ship to users. To be clear about the boundary: a build scanner checks the app you ship, not your Supabase dashboard, so it does not replace enabling RLS or rotating a leaked key on the server side. It catches the client-side exposure; you still have to fix the backend policies.
What to take away
- Which key leaked decides the response: the anon key is meant to be public, the service_role key never is.
- A leaked service_role or secret key is a full database breach; rotate it immediately and move it server-side.
- An exposed anon key is only dangerous without RLS, which is the root cause of CVE-2025-48757 in over 170 Lovable apps.
- Enable and enforce Row Level Security on every table, and verify by querying with the public key yourself.
- Scan each build with PTKD.com so a committed key or secret never ships to users.




