Security Basics
8 min
A key leak rarely starts with someone deliberately publishing a secret. More often, it is a rushed deployment: an AI-generated config file committed to Git, a service-role key copied into a frontend variable, or a preview build that exposes more than production. Knowing how to find exposed API keys is therefore less about hunting for dramatic breaches and more about checking every place your application turns code into a live product.
For a founder, the question is not simply whether a string looks sensitive. It is whether someone who should not have access can use it to read data, alter records, send messages, spend money, or impersonate your service. That distinction matters because not every key in a browser bundle is automatically a vulnerability. Some are intended to be public, but only when their permissions are tightly constrained.
Start by separating public identifiers from real secrets
Before scanning, make an inventory of the services connected to your application. Typical entries include your database platform, authentication provider, payment processor, email sender, AI model provider, cloud account, analytics tool, storage bucket, and error-monitoring service. For each one, identify which credentials belong in the browser, which belong only on the server, and what each credential can do.
This avoids a common and expensive mistake: treating every client-side token as a critical incident, while missing the one that matters. A Supabase anonymous key, for example, may legitimately appear in a web application because it identifies the project to the client. Its safety depends on row-level security (RLS) and the policies behind it. A Supabase service-role key is different. It can bypass RLS and must never reach a browser bundle, public repository, client log, or frontend environment variable.
The same principle applies to payment and AI services. A publishable payment key can be designed for client use, while a secret key can create charges, issue refunds, or inspect customer information. An AI provider key embedded in frontend JavaScript may allow strangers to consume your usage allowance or access services under your account. Permissions, not the label alone, determine severity.
How to find exposed API keys in your repository
Begin with the current codebase, but do not stop there. Search tracked files, ignored files that may have been copied into build contexts, deployment configuration, infrastructure definitions, test fixtures, sample files, and documentation. Pay particular attention to .env files, CI configuration, container definitions, serverless function settings, and files created by scaffolding tools.
Automated secret scanners such as GitLeaks can flag patterns associated with known providers, high-entropy strings, and credentials that fit expected formats. They are useful because they check more consistently than a manual search, especially in a fast-moving repository. But their output needs review. A scanner can report placeholders, revoked tokens, test values, or public identifiers that are not exploitable.
Manual review fills the gap. Inspect files where AI-assisted coding often takes shortcuts: API route handlers, database clients, webhook endpoints, deployment scripts, and quick integration modules. Look for values hard-coded directly into source instead of read from server-side environment variables. Also check whether frontend variables use the framework's public prefix convention. In many JavaScript frameworks, adding the public prefix intentionally places that value in the browser bundle.
Git history deserves separate attention. Removing a key from the latest commit does not remove it from earlier commits, pull requests, forks, release tags, or clones. Scan the full history of repositories you control, including abandoned prototypes and private repositories that may have had broad team access. If a credential was committed, assume it has been exposed to every person and automated system with historical repository access until it has been rotated.
Inspect what your live application actually sends to browsers
A clean repository does not prove that production is clean. Build systems can inject environment variables, old assets can remain on a CDN, and a deployment platform may be configured differently from local development. Review the live application you own in a browser and inspect loaded JavaScript, network requests, source maps, error messages, and client-visible configuration.
The practical test is simple: can an unauthenticated visitor obtain a credential or configuration value that grants more access than intended? Check production, preview, staging, and any forgotten subdomains. Preview environments are frequently overlooked because they are created quickly, shared widely, and sometimes point at real services.
Do not test an exposed key by using it to browse data, create records, issue requests with side effects, or access accounts. That can create operational damage and may breach contractual or legal boundaries. Instead, identify the key's provider and permission scope from your own account settings, logs, and configuration. Where a provider offers a safe key-status check or audit log, use it. The goal is to establish exposure and impact without turning verification into exploitation.
Look beyond source code and browser bundles
Secrets often leak through operational systems rather than application code. Deployment logs can print environment variables after a failed build. Error tracking may capture request headers. Support screenshots may show tokens. A misconfigured object-storage bucket can expose backups, configuration exports, or old .env files. Public package registries and pasted code snippets are also worth checking when your team has shared components externally.
Review the places your team uses to move quickly: CI job output, ticket attachments, chat exports, internal wikis, demo projects, and migration scripts. This work is not glamorous, but it reflects how leaks happen. A key copied into a debugging message is still a key, even if it never touched the main branch.
Triage findings by access, not panic
When you find a suspected exposure, record the file path or URL, the environment, the provider, the credential type, its known privileges, and whether it was reachable publicly. Then decide what it can affect.
A leaked development key with no production access may be lower priority than a production service credential. A public key paired with correctly enforced RLS may require configuration review rather than emergency rotation. Conversely, an anonymous database key becomes serious if RLS is disabled, policies are too broad, or users can manipulate JWT claims to reach another customer's records.
Severity should reflect the realistic business outcome. Can the issue expose customer data? Enable account takeover? Create unauthorised payment activity? Generate an uncontrolled AI bill? Interrupt your service? This framing gives founders a clear order of work instead of a pile of generic alerts.
Revoke first, then remove and investigate
If a secret has reached a public location or an unauthorised party, revoke or rotate it immediately. Do not wait until you have fully cleaned the repository. Replace it in the correct server-side secret store, redeploy affected services, and confirm that the old credential no longer works.
Next, remove the value from source, build artefacts, logs, and documentation. Rewrite repository history only when it is appropriate for your workflow, but do not mistake history rewriting for remediation. The old value may already exist in clones, caches, pull requests, and third-party systems. Rotation is what ends its usefulness.
Afterwards, inspect provider audit logs and billing data from the earliest likely exposure date. Look for unfamiliar IP addresses, unusual API methods, sudden spikes in requests, new users, unexpected data exports, or unusual spend. If the credential could have accessed customer information, assess notification and contractual obligations with the relevant legal and operational advisers.
Make key exposure harder to repeat
The best prevention is structural. Keep privileged credentials on the server, store them in managed environment settings or a secrets manager, and give each environment separate keys. Apply provider restrictions where available, such as permitted origins, IP restrictions, short expiry times, scoped permissions, and usage limits.
Add secret scanning to local commits and CI so a risky change is blocked before deployment. Treat scanner alerts as a review queue, not an automatic verdict. Pair them with code review that asks a direct question: does this variable need to be visible to the browser, and what can it do if copied?
AI-generated code makes this discipline more valuable, not less. Generated integration snippets often optimise for a quick working example and may place configuration in the most convenient file. A working demo is not evidence that a key belongs in client-side code or that database permissions are safe.
If you are unsure whether a discovered value is harmless configuration or a route into production data, pause the release and establish its scope. A focused review of the repository, live URL, RLS policies, authentication flow, and deployment settings is usually faster than responding after a customer or attacker finds it first. HollowByte can assess those paths and provide the exact fixes required, but the immediate rule is simple: any credential you cannot confidently classify should be treated as a production risk until proven otherwise.
Back to blog
