How to Remove Hardcoded Credentials From Source Code
A password or API key written directly into source code is exposed to everyone with repo access - including every git clone, fork, and log entry forever. The fix is one line: read from os.environ instead of hardcoding the value.
Why hardcoded credentials are a critical finding
When a secret is hardcoded in source code, it leaks on every surface:
- git history - even after you remove it, every old commit still has it; anyone with a clone has it forever
- filesystem access - if an attacker reads
app.pyon a compromised server, they get the database password immediately - accidental sharing - a screenshot, a pastebin, a public fork - and the credential is out
The fix is to inject secrets at runtime through the environment and never write them into files that enter version control.
The pattern: os.environ reads
Before - credentials hardcoded in source:
# BAD: anyone who can read app.py has these credentials
DB_PASSWORD = "dev"
API_SECRET_KEY = "s3cr3t-k3y-f0r-t0k3ns"
After - values come from the runtime environment:
import os
DB_PASSWORD = os.environ["DB_PASSWORD"]
API_SECRET_KEY = os.environ["API_SECRET_KEY"]
os.environ["VAR_NAME"] raises KeyError at startup if the variable is missing - which is
the right behavior. It makes a misconfigured deployment fail fast and loudly rather than
running with a blank or wrong credential. Use os.environ.get("VAR_NAME", default) only when
a missing value is genuinely acceptable.
How environment variables get set
In local development, set them in your shell or a .env file that is never committed
(add .env to .gitignore):
export DB_PASSWORD="dev"
export API_SECRET_KEY="s3cr3t-k3y-f0r-t0k3ns"
python3 app.py
In production, secrets are injected by the runtime:
- Docker / Compose -
environment:block or--env-file(still keep the file out of git) - Kubernetes - a
Secretobject mounted as env vars; the pod spec references it by name, never contains the value - AWS / GCP / Azure - managed secrets services (AWS Secrets Manager, GCP Secret Manager) that return the value at runtime via SDK call, never via a file on disk
Adding a pre-commit guard
The longer-term protection is blocking the secret from ever reaching git. gitleaks and
trufflehog both run as pre-commit hooks and match common credential patterns (API key
prefixes, high-entropy strings, known service formats):
# install gitleaks and add a pre-commit hook
gitleaks detect --source . --verbose
A CI check that runs the same scan on every pull request catches anything a developer's local hook missed.
Rotating after a leak
If a secret was already committed, removal alone is not enough. The credential is in every
clone. The required steps are: (1) revoke and rotate the credential immediately, (2) rewrite
history with git filter-repo to scrub the value, and (3) force-push with a coordinated
team heads-up. Assume every clone made before the rotation is compromised.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Replacing hardcoded credential strings with os.environ reads
- Setting environment variables locally and understanding how they are injected in production
- Using pre-commit hooks (gitleaks) to block credential commits before they reach git
FAQ
How do I remove hardcoded credentials from Python source code?
Replace the hardcoded string with os.environ["VAR_NAME"]. This reads the value from the runtime environment at startup. Set the variable in your shell for local dev, and via your deployment platform (K8s Secret, AWS Secrets Manager, etc.) in production.
What's the difference between os.environ[] and os.environ.get()?
os.environ["KEY"] raises KeyError at startup if the variable is missing - good for required secrets because it fails fast. os.environ.get("KEY", default) returns the default if missing, which is appropriate only when a missing value is genuinely acceptable.
If I remove a hardcoded password from git, is it gone?
No. Every commit that contained it still has it in git history, and everyone who cloned the repo before the removal has it locally. You must rotate the credential immediately and optionally rewrite history with git filter-repo to purge the value from all commits.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →