VibeSecurity

Tools and workflow

How to Rotate a Leaked API Key: OpenAI, Supabase, Stripe

This guide is for anyone who just saw an API key in a commit, a screenshot, a shared prompt or a deployed JavaScript bundle and needs to know what to do in the next hour. It covers the keys AI-built apps leak most: OpenAI, Anthropic, Supabase, Stripe, Firebase, Razorpay, GitHub tokens, and the Vercel and Netlify environment variables that hold them, with each revoke and rotate path taken from current vendor documentation. By the end you will be able to kill the exposed key, replace it, purge it from git history, check for abuse, and stop the next leak.

By the VibeSecurity team11 min read

Why revoke comes before everything else

The instinct when you spot a key in a commit is to delete the line and push again. That does nothing. The key still works, and it is still in the history of every clone, fork and cached view. GitHub's guidance on removing sensitive data says that if the data is a secret such as a password, token or credential, you should first revoke or rotate it, because once revoked it can no longer be used for access.

Rewriting history is cleanup, not containment. Treat any key that reached a public repository, a shared chat, a screenshot or a client-side bundle as compromised from the moment it left your machine. Nothing in the providers' documentation gives a safe waiting period, so do not assume you have one. Also note that some keys are not secrets at all: Firebase web API keys and Supabase publishable keys are designed to ship in client code, and for those the fix is the access rules behind them, covered below.

OpenAI and Anthropic keys

OpenAI's safety guidance is blunt: if you believe an API key has been exposed, misused or otherwise compromised, revoke it promptly and replace it. Keys are managed on the API keys page under organization settings. Its production best practices recommend setting an expiration date on every project key and a regular rotation process: create the replacement, update your applications to use it, then revoke the old key once the replacement is verified. Usage per key is visible on the Usage page once tracking is enabled, which is the default for keys created after December 20, 2023.

Anthropic keys live in the Claude Console under Settings and API keys. The Console shows the full key, which starts with sk-ant-, only once at creation, so copy the replacement into your secrets manager immediately. You choose a type and an expiration when creating it. A personal key stops working if you leave the organization; a service account key is the recommended choice for anything shared such as CI or production. For both providers a leaked key means someone else can spend your money until it is revoked.

Supabase: which key did you actually leak?

Supabase has two very different kinds of key. The publishable key, format sb_publishable_..., and its legacy equivalent the anon key, are documented as safe to expose in web pages, mobile apps, GitHub Actions, CLIs and source code. Leaking one is not an incident in itself; your data's security rests on Row Level Security, and if RLS is missing the key being public is the least of your problems. Our Supabase RLS guide covers the fix.

The secret key, format sb_secret_..., and its legacy equivalent service_role, are different. Supabase's docs say a secret key bypasses every Row Level Security policy, because its role carries the BYPASSRLS attribute. It belongs only in backend components such as servers, Edge Functions and APIs with their own authorization checks. If one is in a client bundle, a public repo or a NEXT_PUBLIC_ variable, every row in every table is readable and writable by whoever finds it.

The documented rotation for a secret key is: create a new secret key, replace the compromised key everywhere your application uses it, then delete the leaked key, which cannot be undone. Legacy keys are deactivated instead, and deactivation is reversible if you find a client you missed. Supabase is deprecating the anon and service_role keys by the end of 2026, so an incident is a reasonable moment to move to the new format.

Find a Supabase secret key in your shipped code
grep -rE "sb_secret_|service_role|eyJhbGciOi" .next/ dist/ build/ 2>/dev/null
grep -rE "NEXT_PUBLIC_.*SERVICE|VITE_.*SERVICE|EXPO_PUBLIC_.*SERVICE" .env* src/

Stripe: rotate with a grace period, then expire

Stripe's API keys page lets you create, reveal, expire and rotate keys. Publishable keys, pk_, are safe to expose. Secret keys, sk_, and restricted keys, rk_, are not, and Stripe now recommends restricted keys with only the permissions your integration needs. Its support article on compromised keys lists what an attacker can do with a full secret key: create fraudulent charges or subscriptions, issue refunds including to themselves, and access customer data.

The documented response is to find the key on the API keys page, choose Rotate key, which creates a new key and sets the compromised one to expire, update your backend, confirm it works, then delete the compromised key. When rotating you pick an expiry; Now deletes the old key immediately, which is what a confirmed leak needs. For a planned rotation, both keys work for up to 7 days so you can migrate without downtime.

After rotating, Stripe tells you to review recent usage of the key for unusual activity in Workbench and to reverse any payouts already sent to fraudulent bank accounts. Stripe also recommends an access policy on all live mode keys, so requests from IP addresses or networks you did not approve are blocked and you are notified. Webhook signing secrets are separate from API keys and live under each endpoint in the Webhooks section.

Firebase: the web key is not a secret, but restrict it

Founders regularly panic over a Firebase apiKey in a public config file. Firebase's documentation is explicit: API keys for Firebase services only identify your project and app, and authorization is handled through Google Cloud IAM, Firebase Security Rules and App Check. None of the Firebase-related APIs use an API key as authorization. If your setup follows Firebase's guidelines, keys restricted to Firebase services do not need to be treated as secrets and are safe to include in code.

Firebase states that someone with your key will not be able to access your database or Cloud Storage data as long as it is protected by Security Rules. So the question after a leaked Firebase key is not how to rotate it but whether your Firestore, Realtime Database and Storage rules actually deny strangers. Our Firebase rules guide walks through testing that.

Firebase-provisioned keys are automatically restricted to Firebase-related APIs, managed on the APIs and Services, Credentials page of the Google Cloud console. What is a secret is a service account JSON file or Admin SDK credential; if one is in your repo, treat it like a Supabase secret key: disable it, issue a new one, purge it.

Razorpay keys and GitHub tokens

Razorpay authenticates every API call with a key_id and key_secret pair, and the Dashboard shows only the Key Id after generation. Razorpay warns not to share the secret with anyone or on any public platform. To rotate, select the mode, Test or Live, go to Account and Settings, API Keys, and choose Generate new key. Razorpay lets you deactivate the old key immediately or within 24 hours; for a leak, pick immediately, and check which mode the leaked key belongs to.

GitHub personal access tokens are deleted from Settings, Developer settings, Personal access tokens, under Fine-grained tokens or Tokens (classic), with the Delete button beside the token. GitHub recommends fine-grained tokens, scoped to one owner, specific repositories and specific permissions, with an expiration. Deleting a token that was used to create a deploy key also deletes that deploy key, so check deploy keys afterwards.

If the token was in a public repository, GitHub secret scanning may already have acted: it runs automatically for free on public repositories, and partner providers are notified when their secret is detected so they can take action such as revoking it. Do not rely on that. Revoke it yourself and confirm in the token list that it is gone.

Provider cheat sheet for a leaked key, from each vendor's current documentation
Provider and keyWhere to revokeOld key after rotationWhat to check for abuse
OpenAI project keyPlatform settings, API keys pageStays valid until you revoke itUsage page per key, once tracking is enabled
Anthropic sk-ant- keyClaude Console, Settings, API keysStays valid until deleted or expiredConsole usage and billing pages
Supabase secret or service_roleProject settings, API keysValid until deleted (new) or deactivated (legacy, reversible)Database and auth logs; every table is exposed while it is live
Supabase publishable or anonNot a secret, do not rotate for thisNot applicableWhether RLS is enabled and policies are narrow
Stripe sk_ or rk_Dashboard, API keys, Rotate keyExpires at the time you choose; Now deletes itWorkbench request logs, refunds, payouts
Firebase web apiKeyNot a secret, restrict in Cloud consoleNot applicableSecurity Rules on Firestore, RTDB and Storage
Razorpay key_secretAccount and Settings, API Keys, Generate new keyDeactivated immediately or within 24 hours, your choiceDashboard payments, refunds and settlement activity
GitHub PATSettings, Developer settings, Personal access tokens, DeleteInvalid once deletedRepository and organization audit logs

Update the app: Vercel and Netlify env vars

The new key has to reach your running app without going through git. On Vercel, environment variables can be created as sensitive: the value cannot be read back, editing means supplying a new value, sensitive values of 32 characters or longer are redacted from build logs, and a team owner can make sensitive the default for production and preview.

On Netlify, variables can be marked as containing secret values through the Secrets Controller, only Team Owners can read shared values, and every change lands in the team audit log; if your site is connected to a public repository, check the sensitive variable policy for untrusted deploys. Whatever the platform, anything named NEXT_PUBLIC_, VITE_ or EXPO_PUBLIC_ is compiled into the client bundle by design, so a secret under one of those names is public even in a private repo.

  • Add the new key as a sensitive or secret variable on the host, never in a committed .env file.
  • Redeploy and verify before deleting the old key, unless the leak is confirmed, in which case delete first and accept the downtime.
  • Grep the built client bundle for both keys. If either appears, the variable is misnamed.

Purge the key from git history

With the key dead and the app on its replacement, clean the repository. GitHub recommends git filter-repo with the --sensitive-data-removal flag, which needs at least version 2.47. Run it in a fresh clone, either removing a file from every commit or replacing matching text everywhere, then force-push the rewritten history over the remote.

Two consequences follow. Collaborators must rebase, not merge, any branches they created off the old history, or the secret comes straight back. And a force-push does not clear everything GitHub has: pull request views and cached pages can still show the old commits. GitHub asks you to contact Support with the repository details, affected pull request count and commit hashes, so they can dereference or delete affected PRs and remove cached views. Purging stops future readers finding the key; it does not undo the exposure, which is why the key was revoked in step one.

Purge a committed .env from all history with git filter-repo
git clone --mirror git@github.com:you/your-app.git
cd your-app.git
git filter-repo --sensitive-data-removal --invert-paths --path .env
git push --force --mirror origin

Check for abuse, then stop the next leak

Every provider above has a log: OpenAI per-key usage, Stripe request logs in Workbench, Razorpay's Dashboard by mode, Supabase database and auth logs. Look for requests from regions or hours you do not operate, spikes in spend, refunds you did not issue, and new rows in tables only your backend writes. Write down what you found, even if nothing; a customer or regulator may ask.

Then turn on GitHub push protection. It blocks pushes, GitHub UI commits, file uploads and REST API requests containing a detected secret, and explains the block. For your user account it is on by default for public repositories; for a repository it is off by default and must be enabled by a repository administrator, organization owner, security manager or enterprise owner. Finally, set expirations on replacement keys, and run a read-only external scan such as VibeSecurity against your deployed site so a key that slips into a bundle is caught by you, not a stranger.

  1. 1Open the provider's usage or request log for the leaked key's lifetime.
  2. 2List anything you did not do: requests, charges, refunds, payouts, new rows.
  3. 3Reverse what you can; contact the provider's support for the rest.
  4. 4Enable push protection on the repository.
  5. 5Set an expiration on every replacement key.

Frequently asked questions

Should I delete the key from the commit before revoking it?

No. Revoke or rotate at the provider first. GitHub's guidance on removing sensitive data says to revoke or rotate the secret before cleaning the repository, because once revoked it can no longer be used for access. Removing the line in a new commit leaves the key valid and still present in history, clones and cached views.

Is a leaked Firebase API key an emergency?

Usually not on its own. Firebase documents that its API keys only identify your project and are not used for authorization, and that keys restricted to Firebase services can safely live in code. The emergency is weak Security Rules: check that Firestore, Realtime Database and Storage rules deny strangers, because rules protect the data, not the key.

Does rotating a Supabase anon key fix an open table?

No. The anon and publishable keys are documented as safe to expose, and a new one is just as visible in your bundle. An open table is fixed by enabling Row Level Security and writing narrow policies. Rotate only the secret or service_role key, which bypasses RLS and must never be in client code.

How long does a leaked API key stay valid?

Until you revoke it or it expires. None of the providers covered here document invalidating a key because it appeared in a repository, although GitHub secret scanning notifies partner providers who may revoke it. Assume it is live, revoke it yourself, and confirm in the provider's key list that it is gone.

What does GitHub push protection actually block?

It blocks pushes from the command line, commits made in the GitHub UI, file uploads and REST API requests that contain a detected secret, and explains why. It is on by default for your user account for public repositories, and a repository administrator can enable it per repository in the security settings.

Put it into practice

Sources

  1. 1.OpenAI: Production best practices
  2. 2.OpenAI: Safety best practices
  3. 3.Anthropic: Get your Claude API key
  4. 4.Supabase: Understanding API keys
  5. 5.Stripe: API keys
  6. 6.Stripe: Protecting against compromised API keys
  7. 7.Firebase: Using and managing API keys
  8. 8.Razorpay: Generate API keys
  9. 9.GitHub: Removing sensitive data from a repository
  10. 10.GitHub: About push protection
  11. 11.GitHub: About secret scanning
  12. 12.GitHub: Managing personal access tokens
  13. 13.Vercel: Sensitive environment variables
  14. 14.Netlify: Environment variables overview