BuiltSafely

Home/Trends

Trends

Your host says your secrets may be exposed: a rotation checklist

If your host reports that stored env vars may have been accessed: list every secret and copy, rotate database and backend keys first, revoke old values, handle paused deploys, cap spend, and check provider logs.

  • Docs-only
  • Reviewed Oct 2026
  • 26 official sources
Sources Last checked October 21, 2026 · Badge: docs-only (the local inventory commands in section 1 were tested on a throwaway Git repo with fake keys; every rotation, log and spend-cap step comes from provider docs, and we didn't rotate real keys) · Sources: official Supabase, Stripe, OpenAI, Anthropic, AWS, Google Cloud, GitHub, Resend, SendGrid and Vite docs (linked below) · Educational, not an audit

If your host reports that the environment variables it stores for you may have been accessed, treat every value in them as exposed, even if you're not sure anyone used them. Stripe's guidance for its own keys is the right default for all of them: rotate "immediately even if you aren't sure anyone saw it", and "Treat any exposure as a potential compromise" (Stripe: API key best practices).

This guide is about secrets stored on the host's side. If the problem is a key shipped in your app's JavaScript, see secret keys in frontend env vars. If a package or machine you run may be compromised, see what to do if a dependency gets poisoned.

The short version:

  1. List every secret your app uses and everywhere a copy lives.
  2. Rotate the most dangerous ones first: database passwords and backend secret keys, then payments, email, AI providers, cloud keys, OAuth client secrets and webhook signing secrets.
  3. Revoke each old value once the new one works.
  4. Can't redeploy yet? Rotate at the provider anyway, and keep old keys alive only as long as you must.
  5. Cap spend and set usage alerts on AI and cloud accounts.
  6. Check provider logs for use you don't recognize.
  7. Check your user data if a database or auth key was in the exposed set.

1. List every secret and every copy

Start with a list, not with rotation. A key you forget to rotate stays exposed. For each secret, write down its name, which provider issued it, and every place a copy lives:

  • Your host's dashboard: every environment variable, in every environment (production, preview, staging). Go by what the dashboard lists, not by what you remember adding.
  • CI secrets. In GitHub, a repository's Settings → Secrets and variables → Actions page lists its secrets, and also shows environment and organization secrets it can use. From a terminal: gh secret list, gh secret list --env ENV_NAME and gh secret list --org ORG_NAME (GitHub: Using secrets in GitHub Actions).
  • Backend secret stores, such as Supabase Edge Function secrets (Supabase: Edge Function environment variables).
  • Local files: .env files, backups like .env.bak, and copies on other computers, in password managers, or in notes.
  • Git history: an env file or key that was committed once is still in old commits, even after it's deleted.

These commands cover the local files and Git history. Run them from your project folder:

# 1. Env files and backups in your project folder, including ones git ignores
find . -path ./node_modules -prune -o -type f \( -name '.env*' -o -name '*.env' \) -print

# 2. Env files that were ever committed, even if deleted or ignored later
git log --all --format='%h %ad %s' --date=short --name-only -- '*.env' '**/.env*' '.env*'

# 3. Key formats anywhere in git history (prints commit:file for each hit)
git rev-list --all | xargs git grep -IlE 'sb_secret_|(sk|rk)_live_|whsec_|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}' | sort -u

The patterns match Supabase secret keys (sb_secret_), Stripe live secret and restricted keys (sk_live_, rk_live_), Stripe webhook signing secrets (whsec_), OpenAI-style keys (sk-) and AWS access key IDs, which look like AKIAIOSFODNN7EXAMPLE (Supabase: API keys, Stripe: API keys, Stripe: Webhooks, OpenAI CLI docs, AWS: Manage access keys for IAM users). Add patterns for any other provider you use. A database password has no fixed format, so search for its variable name instead, for example DATABASE_URL.

Verified (git 2.47.3 on Linux): in a throwaway repo with fake keys, command 1 found .env, .env.local, .env.bak, .env.production~, prod.env and supabase/functions/.env, and skipped node_modules. Command 2 found a .env.bak that had been committed and later untracked. Command 3 found a fake whsec_ secret in a file that was deleted in a later commit, and the fake keys in the committed backup. Docs-only for the GitHub steps: we didn't run gh secret list against a real repository for this guide.

A key you forget to rotate stays exposed. Start with the list.
BuiltSafely Research

2. Rotate in order of blast radius

Rotate first whatever would hurt most in someone else's hands. For each secret, the pattern is the same: create the new value at the provider, put it everywhere on your list, confirm it works, then revoke the old one (section 3).

Supabase's own leak procedure starts with "Fix the root cause of the leak before you rotate anything" (Supabase: API keys). Here the root cause is on your host's side, so read your host's notice for whether the exposure is closed before you store new values in the same place.

OrderSecretWhy it's first
1Database passwords and backend secret keys (for example Supabase sb_secret_ or service_role)They can read and change all of your data
2Payment keys (for example Stripe secret and restricted keys)Money and customer payment data
3Email provider keysSending mail as you: phishing your users
4AI provider keysUsage billed to you
5Cloud access keysWhatever the key's permissions allow, including new billable resources
6OAuth client secretsActing as your app's identity
7Webhook signing secretsForged events, such as a fake "payment succeeded"

Move a key up the list if it has broad permissions in your setup. A cloud key with admin rights belongs at the top.

Database passwords and backend secret keys

  • Supabase secret keys (sb_secret_...). A secret key "bypasses every Row Level Security policy you have" (Supabase: API keys). Create a new one in Settings → API Keys, replace the old key everywhere, confirm every component uses the new key, then delete the old one. Deletion "can't be undone", so confirm first (Supabase: API keys). Supabase also suggests a separate secret key for each backend component, so one leak means one rotation (Supabase: API keys).
  • Legacy Supabase service_role key (a long eyJ... JWT). Replace it with a new sb_secret_ key, then deactivate the legacy keys in the same Dashboard section. Deactivation is reversible if you find a client you missed (Supabase: API keys).
  • Don't start with the legacy JWT secret. Regenerating it signs out active users, and the legacy anon and service_role keys "must be rotated simultaneously" (Supabase: JWT signing keys). If only the service_role key was exposed, swapping it for a new secret key "prevents downtime" (Supabase: JWT signing keys). Our step-by-step Supabase rotation covers the case where the JWT secret itself was exposed.
  • Supabase database password. Change the postgres password on the Dashboard's Database Settings page. Supabase says this causes no downtime: its own services pick up the new password automatically, but "external services connecting to the Supabase database using hardcoded username/password credentials" need a manual update (Supabase: Postgres roles). Your app's DATABASE_URL on your host is one of those.
  • Other databases: use your database provider's password reset, then update every connection string on your list.

Payment keys

  • Stripe secret and restricted keys. In the Dashboard's API keys page, open the key's overflow menu, select Rotate key, and choose an expiration. Now deletes the old key at once. A later time keeps both keys working, for up to 7 days (Stripe: API keys). Save the new value right away: "You can't retrieve it later" (Stripe: API keys).
  • If your host manages your Stripe key for you, Stripe says to rotate it "from the Stripe Dashboard or on the server platform". A rotation in the Dashboard syncs to the platform, so you don't change it in two places (Stripe: API key best practices).
  • Next time, use restricted keys with only the permissions each part of your app needs, to "limit the potential impact of a compromise" (Stripe: API key best practices).

Email provider keys

  • Resend: the API keys page shows when each key was last used. You can't view a key's value after it's created, so create a new key, deploy it, then use Remove API key on the old one (Resend: Manage API keys).
  • SendGrid: in Settings → API Keys, create a new key (it's shown "only once at creation"), deploy it, then Delete API Key on the old one. SendGrid "rejects any subsequent API calls made using this deleted API key" (Twilio SendGrid: API keys).

AI provider keys

  • OpenAI: create a replacement, update your apps, and revoke the old key "once you've verified that the replacement works" (OpenAI: Production best practices).
  • Anthropic: on the Claude Console's API keys page, Disable is reversible and Delete is permanent. Anthropic says to "disable or delete any key you suspect has leaked" (Anthropic: Authentication).
  • Google (Gemini API and Vertex AI) steps, and more detail for all three, are in our AI provider key rotation fix.

Cloud access keys

OAuth client secrets

  • Google OAuth clients: open the client in the Google Cloud console and select Add Secret. "Both secrets can be used until you manually disable them", and a client can have two at most. Update your app, confirm the old secret isn't used anywhere, then disable and delete it (Google Cloud: Manage OAuth clients).
  • For other sign-in providers, look for the same pattern in their docs: add a second secret, switch, then remove the first.

Webhook signing secrets

  • Stripe: in Workbench's Webhooks tab, open each endpoint, then select Roll secret from the overflow menu. You can expire the old secret now or "delay its expiration for up to 24 hours". While both are active, Stripe signs each event once per secret (Stripe: Webhooks). Our Stripe webhook signature fix covers verification errors after the switch.
  • GitHub webhooks: set a new secret token on the webhook, with "a random string of text with high entropy", and update the value your server checks (GitHub: Validating webhook deliveries).

Docs-only: we didn't rotate real keys for this guide. The steps come from each provider's docs, checked October 21, 2026.

3. Revoke old values once the new ones work

Rotation isn't done until the old value stops working. Until then, whoever has it can still use it.

  • Check that nothing still uses the old value. Stripe suggests checking the old key's request logs and expiring it "only after its request volume has been at zero for a few hours or days" (Stripe: API keys). AWS and Resend show when a key was last used (AWS: Update access keys, Resend: Manage API keys).
  • Disable before you delete, where you can. Supabase legacy keys can be deactivated and re-enabled, AWS access keys can be made inactive, and Anthropic keys can be disabled (Supabase: API keys, AWS: Update access keys, Anthropic: Authentication). That lets you undo a mistake. Deleting a Supabase secret key can't be undone (Supabase: API keys).
  • Update your list. Mark each secret as rotated and revoked, and note where the new value lives. Delete stale .env backups instead of updating them.
  • Mark keys as sensitive. If your host lets you mark environment variables as sensitive, Stripe recommends doing that for Stripe keys (Stripe: API key best practices).

Docs-only: from the providers' docs; we didn't revoke real keys for this guide.

4. If you can't redeploy right now

If your host has paused deploys or builds, you can't push new values to your app yet. You still have options, and each one trades safety against uptime.

  1. Rotate at the provider anyway, and revoke the old key now. This is the safest choice. The exposed key stops working at once, but so do the parts of your app that use it, until you can deploy the new value.
  2. Rotate now, and keep the old key alive for a short, set time. Some providers let old and new values overlap: Stripe API keys for up to 7 days, Stripe webhook secrets for up to 24 hours, Google OAuth client secrets and AWS access keys until you disable the old one (Stripe: API keys, Stripe: Webhooks, Google Cloud: Manage OAuth clients, AWS: Update access keys). Your app keeps working, but so would the exposed key, for anyone who has it. Choose the shortest overlap you can, and set a spend cap (section 5) to cover that window.
  3. Set the new value on the host now, and restart or redeploy when you can. Check your host's docs for whether a changed variable takes effect on restart or only on a new deploy. Values your frontend reads at build time, like VITE_ variables, change only after a rebuild, because they're "statically replaced at build time" (Vite: Env variables and modes).

Secrets that live outside your host don't need a redeploy there. Supabase Edge Functions "read a new secret immediately" (Supabase: Edge Function environment variables), and changing the Supabase database password updates Supabase's own services automatically (Supabase: Postgres roles).

The plain tradeoff: every hour an exposed key stays valid is an hour someone else can use it. A short outage is usually cheaper than a drained account or a data leak, but you know which features you can afford to switch off. Write down which keys you left alive, and when you'll revoke them.

Docs-only: the overlap periods and the Edge Function behavior come from the providers' docs. We didn't test a paused host for this guide.

5. Cap spend and set usage alerts

A spend cap limits what an exposed key can cost you, and an alert tells you when someone is using it. Alerts alone don't stop anything.

  • OpenAI: in Project settings, select Limits, then Edit spend limit, and turn on Enforce a hard limit. Organization limits work the same way. "Spend alerts do not enforce a cap", and spend "can slightly exceed" a hard limit, because enforcement isn't instant (OpenAI: Spend limits).
  • Anthropic: on the Claude Console's Workspaces page, select the workspace, then Spend limits, "to cap monthly spending and configure alerts" (Anthropic: Workspaces).
  • AWS: AWS Budgets can alert you on actual or forecasted costs, and can apply an action when a threshold is reached, for example an IAM policy that blocks new resources (AWS: Managing your costs with AWS Budgets).
  • Google Cloud: an alerts-only budget "doesn't automatically set a hard cap on spending". Some services support a spend cap budget instead (Google Cloud: Create, edit, or delete budgets and budget alerts). Gemini API spend caps are in our AI provider key fix.

Docs-only: we didn't set caps on paid accounts for this guide.

6. Check provider logs for use you don't recognize

Look at the time between when the exposure may have started and when you revoked each key. Anything you can't explain is worth a closer look, and worth reporting to the provider.

  • Stripe: from a key's overflow menu, View request logs opens that key's requests in Workbench (Stripe: API keys). The Logs page can filter by date, status, method, endpoint, source and IP address (Stripe: Request logs).
  • Supabase: the Logs view covers API Gateway, Postgres, Auth, Storage, PostgREST, Edge Function and Realtime events. You can filter by status, method, path, user and message text, and download up to 1,000 rows as CSV or JSON. Supabase warns that "An empty result does not establish that the user had no activity", and that retention depends on your plan (Supabase: Logs).
  • OpenAI: key usage shows on the Usage page once tracking is on. Keys made before Dec 20, 2023 don't track by default (OpenAI: Production best practices).
  • Anthropic: the Console's Usage page breaks usage down "by model, date/time, and API key", with CSV export (Claude Help: Cost and usage reporting).
  • Resend: separate keys let you "view logs per key" and "detect possible abuse" (Resend: Manage API keys).
  • AWS: AWS recommends CloudTrail "to monitor access key usage and detect any unauthorized access" (AWS: Manage access keys for IAM users).

Docs-only: we didn't review logs on real accounts for this guide.

7. When your users' data might be at risk

If a database password, a Supabase secret or service_role key, or another key with full data access was in the exposed set, someone could have read or changed your users' data without going through your app. Check for:

  • Unusual database and API activity. In Supabase Logs, look at Postgres and API Gateway events in the exposure window, especially large reads, requests to tables your app doesn't normally touch, and traffic at odd hours (Supabase: Logs).
  • Unusual sign-ins and account changes. Supabase's auth audit logs record sign-ups, sign-ins, password changes and resets, and token refreshes, to help "detect suspicious behavior". You can also store them in the auth.audit_log_entries table and search them with SQL (Supabase: Auth audit logs).
  • New admins and changed roles. Look for accounts you didn't create, and for any user whose role or admin flag changed in the exposure window. If you use a roles table, check it too.
  • Changed rules and data. Look for access rules, policies, functions or webhooks you didn't add, and for records that were changed or deleted in bulk.
  • Exports and backups. Look for downloads or exports of your data that no one on your team made.

If you find signs that personal data was accessed, write down what you found and when, and keep copies of the logs, since some logs are only kept for a limited time (Supabase: Logs). Depending on where you and your users are, you may have to notify users or a regulator. This guide isn't legal advice, so get qualified advice on that.

Docs-only: the Supabase log and audit-log features come from Supabase's docs. The other checks are general starting points, not a full investigation.

Your turn

What was hardest to find when you made your list: CI secrets, old .env backups, or keys nobody remembered adding? Tell us. We'll add the common hiding spots to this checklist.

This guide is educational and based on the vendor's public docs as of the review date. Platforms change: check the linked sources. Not a security audit. Disclaimer.

The BuiltSafely weekly

One email a week. What changed, and the one fix that matters.

Platform changes that affect the security of AI-built apps, checked against official docs. Free. Unsubscribe any time.