Skip to content

All lessons

Area 06 // AI SecurityLesson 99 / 14013 min read

Secrets management and API key rotation

Secrets such as model-provider API keys must live in a secrets manager or the platform's secret store (never in code, prompts, logs or images), be scoped and given to each service at runtime, and be rotated on a schedule and immediately after any leak, with an overlap period so nothing breaks.

Think of it like this

Analogy

A building's master keys don't hang on a hook by the door. They're in a locked key cabinet; staff sign one out for a shift and return it. Each key opens only what's needed. When a key goes missing, the locks are changed, and the old key stops working. Changing the locks every so often anyway means a quietly copied key doesn't work forever.

The problem it solves

AI apps are full of valuable secrets: model-provider keys (which can run up thousands in charges), vector-database credentials, cloud keys, OAuth client secrets. They leak through git commits, frontend bundles, Docker images, CI logs, screenshots, traces, and now through prompts, since anything placed in a model's context can come back out. Leaked LLM keys are actively hunted and abused within minutes.

How it works: secrets management

  1. Keep secrets out of code and repos: load them from the environment or a mounted file; add .env to .gitignore and commit only a .env.example with placeholders (environments and config).
  2. Store them in a secrets manager: a cloud secrets service, HashiCorp Vault, or your platform's secret store (e.g. GitHub Actions secrets, your host's environment settings). Access is logged and permissioned.
  3. Inject at runtime: the platform passes secrets to the running service; fail fast at startup if one is missing (see the code).
  4. Scope them: one key per service and environment (dev, staging, prod), minimal permissions, provider-side spending limits and usage alerts.
  5. Keep keys server-side: browsers and mobile apps call your backend, which calls the model; never ship provider keys to clients.
  6. Keep secrets out of model context: tools fetch what they need using credentials the model never sees (least privilege); redact secrets from logs and traces.
  7. Scan: pre-commit hooks and repository push protection (e.g. GitHub secret scanning) block commits containing key-shaped strings; scan prompts and logs too.

How it works: API key rotation

  1. Schedule rotation (e.g. every 90 days, or as policy requires) and rotate immediately when people leave or a leak is suspected.
  2. Rotation steps: create a new key → store it in the secrets manager → deploy services so they pick it up → overlap period where both work → revoke the old key → confirm no traffic used it.
  3. For keys you issue (your own API for customers), support two active keys per client and an overlap window (see the code).
  4. After a leak, revoke first: kill the exposed key, replace it, then check usage logs for abuse and find how it leaked.
  5. Prefer short-lived credentials where possible (workload identity, OIDC from CI to the cloud), so there's little to rotate.
  6. Automate: rotation scripts or the secrets manager's built-in rotation, with alerts when keys get old.
Rotation without downtime: both keys work during the overlap, then the old one is revoked.

See it in code

Needs
Nothing: plain Python 3. The keys are obviously fake placeholders.

Fail-fast loading from the environment, an overlap window for rotating keys you issue, and a simple secret scanner.

load_secrets.py
import os, re, time
 
# SECRETS MANAGEMENT: read keys from the environment (filled by a secrets manager), fail fast if missing.
def require(name: str) -> str:
    value = os.environ.get(name)
    if not value:
        raise RuntimeError(f"{name} is not set")      # never fall back to a key in the code
    return value
 
os.environ["LLM_API_KEY_CURRENT"] = "key-v2-example"   # in production: injected by the platform
os.environ["LLM_API_KEY_PREVIOUS"] = "key-v1-example"
print("loaded:", require("LLM_API_KEY_CURRENT")[:7] + "...")
 
# API KEY ROTATION for keys YOU issue: accept old and new during an overlap window, then retire the old one.
ROTATED_AT, OVERLAP_S = time.time() - 3_600, 24 * 3_600       # rotated 1 hour ago, 24h grace period
def key_is_valid(presented: str, now: float) -> str:
    if presented == os.environ["LLM_API_KEY_CURRENT"]:
        return "valid (current)"
    if presented == os.environ["LLM_API_KEY_PREVIOUS"] and now - ROTATED_AT < OVERLAP_S:
        return "valid (previous, still in overlap window)"
    return "rejected"
print(key_is_valid("key-v1-example", time.time()))
print(key_is_valid("key-v1-example", time.time() + 2 * 24 * 3_600))
 
# SECRET SCANNING: block commits/prompts/logs that contain something shaped like a key.
SECRET_RE = re.compile(r"(sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----)")
snippet = 'client = OpenAI(api_key="sk-' + "x" * 24 + '")'
print("secret found in code:", bool(SECRET_RE.search(snippet)))
Output
loaded: key-v2-...
valid (previous, still in overlap window)
rejected
secret found in code: True

Only print a prefix of a key, never the whole thing. Real scanners (gitleaks, trufflehog, GitHub secret scanning) know hundreds of key formats and verify some of them with the provider.

A real example

Example

The weekend bill

A developer pasted a provider key into a public notebook while debugging on Friday. By Saturday morning, scrapers had found it and were reselling access; the account racked up about $14,000 in charges before a spending alert fired.

Afterwards the team moved every key into their cloud secrets manager, created separate keys per service with monthly spending caps, enabled push protection on all repos, added a pre-commit secret scanner, set alerts at 50% of each key's normal daily spend, and wrote a one-page "key leaked" runbook: revoke, replace, check usage, report.

The easy way

Start here

Put keys in your hosting platform's secret settings (or a .env file that is git-ignored for local work), read them with os.environ, set a monthly spending limit on every provider key, and turn on secret scanning with push protection for your repositories.

When you grow: the production setup

Teams use a central secrets manager with fine-grained access and audit logs, workload identity and OIDC instead of static keys where possible, automated rotation with overlap, per-service and per-environment keys with budgets, secret scanning in pre-commit, CI and on prompts and logs, and a tested leak-response runbook.

Trade-offs

What you gain

  • Leaks become rare, small and short-lived.
  • Access to secrets is audited.
  • Rotation becomes routine instead of a fire drill.

What it costs you

  • More setup than a key in a config file.
  • Rotation needs coordination across services.
  • Secrets managers are critical dependencies themselves.

When not to use it

  • Never skip it for real keys; even prototypes should keep keys out of git and set spending limits.

Common mistakes

  • Committing .env files or keys in notebooks.
  • Shipping provider keys in frontend or mobile code.
  • Putting credentials or internal codes in system prompts.
  • One shared key for every service, person and environment.
  • "Deleting" a leaked key from git history instead of revoking it.

Interview questions

Try answering out loud first, then open the model answer.

How do you manage API keys for LLM providers in production?

Model answer

Store them in a secrets manager or platform secret store, inject at runtime and fail fast if missing, keep them server-side only, use one key per service and environment with spending limits and alerts, keep them out of prompts, logs and traces, scan for leaks in commits and logs, and rotate on a schedule and after any suspected exposure.

Walk through rotating an API key without downtime.

Model answer

Create the new key; store it in the secrets manager; deploy services to use it while the old key still works; monitor until no traffic uses the old key; revoke the old key; confirm and document. For keys you issue, support two active keys per client with an overlap window and communicate deadlines.

A key has leaked publicly. What do you do?

Model answer

Revoke it immediately and issue a replacement through the secrets manager; check provider usage logs for abuse and contact the provider about fraudulent charges; find and fix how it leaked (scanning, process); rotate any other secrets exposed the same way; and record the incident.

Quiz

Pick an answer to see whether it’s right and why.

  1. 01Where should an LLM provider API key used by your backend live?
  2. 02A key was committed to a public repo and removed in the next commit. Is it safe now?
  3. 03Why have an overlap window during rotation?
  4. 04Why one key per service and environment?
  5. 05What should never contain secrets in an AI app?

0 of 5 answered

Key terms

Secret
Any credential that grants access, such as an API key, password, token or private key. It must never appear in code, prompts, logs or images.
Secrets manager
A service that stores secrets encrypted, controls and logs who can read them, and delivers them to apps at runtime. Examples include cloud secret stores and HashiCorp Vault.
Key rotation
Replacing a key with a new one on a schedule or after a leak, usually with a short overlap so services can switch without downtime, then revoking the old key.
Secret scanning
Automatically searching code, commits, logs or prompts for strings that look like keys or passwords, and blocking or alerting when one is found.
Environment variable
A named value set outside your code (in the shell, a .env file or a server's settings), used for secrets and config.

Before and after

Progress is saved in this browser only.