What to Do When Django Secrets Leak

Respond to a leaked Django SECRET_KEY, database password, or API key: assess exposure, rotate in the right order, use SECRET_KEY_FALLBACKS only when appropriate, invalidate sessions and tokens, purge Git history, and prevent the next leak.

Problem statement

A .env file was pushed to a public repository, a DEBUG = True page exposed settings, a CI log printed environment variables, or a former contractor still has the production credentials. Secret scanners (GitHub's, or attackers') find leaked keys within minutes of publication. What matters now is speed and order: rotate what an attacker could use, invalidate what they could have forged or captured, check for misuse, and close the hole that leaked it, without causing a self-inflicted outage.

Responding to a leakContainstop the exposureRotatekeys and passwordsInvalidatesessions, signed dataInvestigatelogs, then scanningTreat leaked secrets as compromised: rotate first, clean history second, then find the cause.
  1. Assume compromise. Rotating is cheaper than investigating whether the secret was used.
  2. Rotate external credentials first (database, cloud keys, payment, email APIs), because they grant direct access.
  3. Rotate SECRET_KEY without fallbacks if it leaked: sessions, password reset tokens, and signed cookies become invalid, which is the point.
  4. Revoke API tokens and OAuth tokens that were exposed; force password resets if password hashes leaked.
  5. Remove the secret from the repository and history, but rotate anyway; forks and caches keep copies.
  6. Check logs for misuse, fix the cause, and add secret scanning.

Step-by-step solution

1. Contain and assess

Make the repository private or remove the file, disable DEBUG, delete exposed CI logs. Then list every secret in the leaked material: the whole .env, not just SECRET_KEY. Note when it was exposed and where (public repo, internal chat, logs).

2. Rotate external credentials

Order by blast radius:

  • Cloud provider keys (AWS access keys can create resources and read buckets): deactivate the leaked key and issue a new one, or better, move to roles.
  • Database password: change it (ALTER ROLE app PASSWORD '...'), update the app's secret, restart, then confirm old connections are gone.
  • Payment, email, storage API keys: roll them in each provider's dashboard (Stripe supports rolling keys with a grace period; for a leak, use a short or no overlap).
  • Third-party webhooks secrets: regenerate.

Update the secret store first, then restart processes so they pick up new values.

rotation impact by secretActionSide effectCloud access keysโœ•deactivate, reissuenone if roles usedDatabase passwordโœ•change, restart appbrief reconnectSECRET_KEY!new key, no fallbackusers logged out, links expirePayment / email API keys!roll in dashboardupdate integrationsUser password hashesforce password resetsuser frictionRotate direct-access credentials first; session invalidation is intendeddjango-deployment.com
Use fallbacks only for routine rotation, never for a key that leaked.

3. Rotate SECRET_KEY properly

SECRET_KEY signs sessions, password reset links, messages, and anything using django.core.signing. With the key, an attacker can forge session cookies for any user if you use signed-cookie sessions, and create valid signed values. For a leak:

SECRET_KEY = env("SECRET_KEY")          # new value
SECRET_KEY_FALLBACKS = []               # do NOT keep the leaked key

All users are logged out and outstanding reset links stop working. That is acceptable and necessary. SECRET_KEY_FALLBACKS is for planned rotation of a key that is still secret; see Rotate Django SECRET_KEY Safely.

4. Invalidate tokens and sessions

  • Delete all sessions if you use database or cache sessions: python manage.py shell -c "from django.contrib.sessions.models import Session; Session.objects.all().delete()" (or flush the session cache).
  • Revoke DRF tokens, JWT signing keys (rotating the key invalidates issued tokens), and OAuth refresh tokens.
  • If the database itself was exposed, force password resets and notify users as your legal obligations require.

5. Clean the repository

Remove the secret from history with git filter-repo or BFG, force-push, and ask collaborators to re-clone. Contact GitHub support to purge cached views if needed. This reduces future exposure but cannot recall copies already taken, which is why rotation comes first.

6. Investigate and prevent

Check cloud audit logs (CloudTrail), database logs, payment provider logs, and Django logs for activity during the exposure window. Then fix the cause: add .env to .gitignore, enable GitHub secret scanning and push protection, add gitleaks to pre-commit and CI, move secrets to a manager, and make sure DEBUG cannot be enabled in production.

leak response, in order1Contain the exposureprivate repo, DEBUG off2Rotate direct-access credentialscloud, database, APIs3New SECRET_KEY, no fallbacksessions invalidated4Revoke tokens, purge historyDRF, JWT, OAuth, git5Review logs, add scanninggitleaks, push protectionRotation comes before cleanup, because copies already existdjango-deployment.com
Write down what happened; it becomes part of your incident runbook.

Verification check: the leaked values no longer work (test the old database password and API keys and confirm they are rejected), the app runs on new secrets, old session cookies are rejected, and secret scanning now blocks a test commit containing a fake key.

Explanation

A secret's value lies in being unknown; once exposed, it must be treated as known to attackers, because automated scanners harvest public repositories and logs continuously. Credentials that grant direct access to data or infrastructure carry the highest risk, so they are rotated first. Django's SECRET_KEY protects integrity rather than granting access, but forged sessions and tokens can amount to account takeover, so it must be replaced without keeping the old key valid. Cleaning history and adding scanning reduce future risk but do not undo the exposure.

Edge cases / notes

  • Encrypted fields using keys derived from SECRET_KEY (some third-party packages) need data re-encryption with a migration before rotating; check before you rotate.
  • Multiple environments that shared a key all need rotation.
  • Legal and notification duties may apply if personal data could have been accessed.
  • Practice. A rotation drill in staging makes the real event faster and calmer.

Next steps

FAQ

What should I do if my Django SECRET_KEY leaked?

Generate a new key, deploy it without the old one in SECRET_KEY_FALLBACKS, and accept that users are logged out.

Is removing the secret from Git history enough?

No. Copies may already exist. Rotate the secret, then clean history.

Can attackers log in with a leaked SECRET_KEY?

They can forge signed data, including sessions with the signed-cookie backend and password reset tokens in some cases. Rotate immediately.

How do I prevent secrets leaking into Git?

Use .gitignore, secret scanning with push protection, gitleaks in pre-commit and CI, and a secrets manager.

Keep going