Your application sends emails, takes payments, queries a mapping service or calls an AI model. For each of those services it holds a key, a long string of characters proving it is really you. That key works like a means of payment: whoever holds it can spend on your behalf.

In applications produced by generation tools, these keys very often end up in the wrong place.

The wrong place, concretely

A web application has two halves. The code that travels to the visitor’s browser, and the code that stays on a server nobody can reach. Everything in the first half is readable: opening the browser developer tools, present in Chrome and Firefox for years, is enough to browse what was sent.

A key placed in that first half is therefore public. Not because someone stole it, but because it was handed to every person who opened the page.

Generation tools naturally produce browser code, since that is where the interface lives. When you ask them to add an email send or a payment, they put the call where they are already working. The result works perfectly, which is exactly the problem: nothing flags the mistake.

What happens next

Programs continuously crawl the web looking for these strings. Keys from the major services have recognisable formats, and there are tools whose only purpose is to find them.

Consequences vary by service. An email delivery key is used to send junk mail from your domain, which damages your sender reputation for a long time and eventually pushes your genuine messages into your customers’ spam folders. A key for an AI model gets consumed in bills that climb fast. A payment service key gives access to your transactions.

The worst case remains the database administration key. That one bypasses every access rule, including those correctly written, and Supabase states it must never touch browser code.

Telling public keys from the rest

Not every key is a secret, and confusing the two leads to pointless fixes. Some services deliberately hand out a key meant for the browser, designed to be seen. Supabase’s public key falls into that category, as do the publishable keys of payment providers, which usually start with an explicit marker.

Those public keys are harmless on one condition: that the intended protection sits behind them. A public database key with no access policy is an open door, which the CVE-2025-48757 flaw made visible in 2025 across more than 170 applications built with Lovable.

The practical rule: if the service documentation calls it a secret key, a private key or a service key, it has no business in the browser.

How to check in five minutes

Open your application in Chrome or Firefox. Press F12 to bring up the developer tools, then open the sources tab. Use the global search and type the usual prefixes: sk_, secret, api_key, service_role, password.

Look at the network tab too while you use the application. Every outgoing call appears there with its headers. A key sent from the browser to a third-party service shows up in the clear.

If you find something, note it without posting it anywhere else, and treat that key as compromised.

The fix, in three steps

Revoke first. An exposed key stays valid until it is revoked, and nobody knows who already picked it up. Replace it before anything else. This happens in the service’s own dashboard and takes minutes.

Move the call next. The service is no longer called from the browser but from a server endpoint that holds the key and that the browser cannot read. The screen asks the server, the server calls the service. Visible behaviour stays identical.

Verify last. Search the browser code again, and review the service’s usage history to spot abnormal activity predating the revocation.

One point often missed: if your project’s code lives in a repository, the key is in its history too, even after being removed from the file. Cleaning that history is part of the work.

Keys are not the only secrets that leak

The same defect affects other, less obvious pieces of information.

Connection strings. A database address carrying a username and password, sitting in browser code, grants direct and complete access.

Notification secrets. When an outside service tells your application a payment succeeded, a shared secret proves the message really came from it. Without that check, or with an exposed secret, anyone can announce a payment that never happened.

Test accounts left active. An administrator account created during development, with a simple password, often survives go-live.

Internal addresses. An administration endpoint reachable without authentication because it was convenient during testing stays reachable afterwards.

None of these show up in normal use of the application, which is what makes them persist.

What we do

We go through your application’s code, build the list of exposed keys and what each one allows, and hand it to you in plain language with how urgent each case is.

We then move the affected calls to the server side, with a fixed quote based on the scope you approve. Revocation stays in your hands, on your accounts, and we tell you exactly where to click.