SSH Keys, API Keys, and .env Files -- The Developer Security Guide You Actually Need
SSH Keys, API Keys, and .env Files -- The Developer Security Guide You Actually Need
Every developer has done it at least once -- accidentally committed an API key to a public repository, pushed a .env file with database credentials, or left a secret hardcoded in source code. Some developers learn this lesson from a blog post. Others learn it from a surprise AWS bill.
This guide covers the security fundamentals that every developer needs: SSH keys, API key management, environment variables, and the practices that keep your credentials safe.
SSH Keys -- Stop Typing Your Password
SSH keys replace password authentication with cryptographic key pairs. Instead of typing your GitHub password every time you push, your machine proves its identity with a key.
How SSH Keys Work
An SSH key pair consists of two files:
- •Private key -- stays on your machine, never shared with anyone. This is like your house key.
- •Public key -- uploaded to services like GitHub, servers, etc. This is like the lock on your door.
When you connect to a server, your machine uses the private key to prove that it owns the corresponding public key. No password is transmitted.
Generating an SSH Key
> ssh-keygen -t ed25519 -C "your.email@example.com"
When prompted:
- •File location: Press Enter to accept the default (~/.ssh/id_ed25519)
- •Passphrase: Enter a strong passphrase. This encrypts your private key on disk, so even if someone copies the file, they cannot use it without the passphrase.
This creates two files:
- •~/.ssh/id_ed25519 -- your private key (never share this)
- •~/.ssh/id_ed25519.pub -- your public key (safe to share)
Adding Your Key to the SSH Agent
The SSH agent remembers your passphrase so you do not have to type it every time:
> eval "$(ssh-agent -s)"
> ssh-add ~/.ssh/id_ed25519
On macOS, add this to your ~/.ssh/config to use the Keychain:
> Host *
> AddKeysToAgent yes
> UseKeychain yes
> IdentityFile ~/.ssh/id_ed25519
Adding Your Key to GitHub
Copy your public key:
> cat ~/.ssh/id_ed25519.pub
Go to GitHub -> Settings -> SSH and GPG Keys -> New SSH Key. Paste the public key and save.
Test the connection:
> ssh -T git@github.com
You should see: "Hi username! You've authenticated successfully."
Switch Your Repos to SSH
If your repositories use HTTPS URLs, switch them to SSH:
> git remote set-url origin git@github.com:username/repo.git
Now you can push and pull without entering a password.
Multiple SSH Keys
If you have separate keys for work and personal accounts, use your SSH config:
> # Personal GitHub
> Host github.com-personal
> HostName github.com
> User git
> IdentityFile ~/.ssh/id_ed25519_personal
>
> # Work GitHub
> Host github.com-work
> HostName github.com
> User git
> IdentityFile ~/.ssh/id_ed25519_work
Then clone repos with the alias:
> git clone git@github.com-work:company/repo.git
API Keys -- Treat Them Like Passwords
API keys grant access to services. Treat every API key like a password -- because to the service, it is one.
Rules for API Keys
- 1Never hardcode API keys in source code. Not even temporarily. Not even in a test file. Not even in a branch you plan to delete.
- 2Never commit API keys to Git. Even if you delete them in the next commit, they remain in the Git history forever. Bots actively scan GitHub for exposed keys.
- 3Use environment variables to pass keys to your application at runtime.
- 4Rotate keys regularly. If you suspect a key has been exposed, rotate it immediately.
- 5Use separate keys for development and production. Never use your production Stripe key in development.
- 6Set the minimum required permissions. If an API key only needs read access, do not give it write access.
What Happens When Keys Leak
This is not hypothetical. Real consequences include:
- •AWS keys: Cryptominers spin up hundreds of instances. Developers have received bills for $50,000+ within hours.
- •Stripe keys: Attackers can process fraudulent payments or access customer data.
- •Database credentials: Complete data breach. Your users' data is stolen.
- •SendGrid/Mailgun keys: Attackers send spam from your domain, getting you blacklisted.
GitHub scans every push for known key formats and notifies the service provider. But this is a safety net, not a strategy.
Environment Variables and .env Files
Environment variables are the standard way to pass configuration and secrets to your application without putting them in code.
The .env File
Create a .env file in your project root:
> DATABASE_URL=postgres://user:password@localhost:5432/myapp
> REDIS_URL=redis://localhost:6379
> API_KEY=sk_test_abc123
> JWT_SECRET=your-jwt-secret-here
> SMTP_HOST=smtp.mailtrap.io
> SMTP_USER=your-smtp-user
> SMTP_PASS=your-smtp-password
Loading .env Files
Node.js -- use the dotenv package:
> npm install dotenv
At the top of your entry file:
> require("dotenv").config();
> // or with ES modules:
> import "dotenv/config";
Access variables:
> const dbUrl = process.env.DATABASE_URL;
Python -- use python-dotenv:
> pip install python-dotenv
> from dotenv import load_dotenv
> import os
>
> load_dotenv()
> db_url = os.getenv("DATABASE_URL")
The Golden Rule: Never Commit .env Files
Add .env to your .gitignore immediately:
> # .gitignore
> .env
> .env.local
> .env.production
> .env*.local
Create an .env.example File
Instead of committing actual secrets, commit a template file called .env.example with placeholder values:
> DATABASE_URL=postgres://user:password@localhost:5432/myapp
> REDIS_URL=redis://localhost:6379
> API_KEY=your-api-key-here
> JWT_SECRET=generate-a-random-string-here
This tells other developers which variables they need without exposing actual values. Include setup instructions in your README.
Checking for Leaked Secrets
In Your Git History
If you think you might have committed a secret, check:
> git log --all --full-history -p -- .env
> git log --all --full-history -p -S "your_api_key_here"
The second command searches all commits for a specific string.
Removing Secrets from Git History
If you find a committed secret, just deleting it in a new commit is not enough. The old commit still contains it.
Option 1: Use git-filter-repo (recommended):
> pip install git-filter-repo
> git filter-repo --path .env --invert-paths
Option 2: Use BFG Repo-Cleaner:
> bfg --delete-files .env
> git reflog expire --expire=now --all
> git gc --prune=now --aggressive
After either option, force-push to overwrite the remote history. Then immediately rotate every exposed credential.
Pre-Commit Hooks
Use tools like git-secrets or detect-secrets to prevent accidental commits:
> # Install git-secrets
> brew install git-secrets
>
> # Set up in your repo
> git secrets --install
> git secrets --register-aws
This adds a pre-commit hook that blocks commits containing AWS key patterns.
Secret Management in Production
For production deployments, .env files are not enough. Use proper secret management:
Platform-Native Solutions
- •Vercel/Netlify: Built-in environment variable management in the dashboard. Set variables per environment (development, preview, production).
- •GitHub Actions: Repository secrets and environment secrets (Settings -> Secrets)
- •Docker: Use Docker secrets or pass environment variables at runtime, never bake them into images.
Dedicated Secret Managers
For larger applications:
- •HashiCorp Vault -- the industry standard for secret management
- •AWS Secrets Manager -- tight integration with AWS services
- •Google Secret Manager -- same for GCP
- •Azure Key Vault -- same for Azure
- •Doppler -- developer-friendly, works with any platform
These tools provide:
- •Centralized secret storage
- •Automatic rotation
- •Access logging and audit trails
- •Fine-grained access control
Security Checklist for Every Project
Run through this checklist at the start of every project:
- •.env is in .gitignore
- •.env.example exists with placeholder values
- •No secrets are hardcoded in source code
- •SSH keys are set up for Git authentication
- •API keys use minimum required permissions
- •Different credentials for development and production
- •Pre-commit hooks scan for secrets
- •Team members know how to handle a key leak
What to Do When You Leak a Secret
It will happen eventually. When it does:
- 1Rotate the credential immediately. Do not wait. Generate a new key and invalidate the old one.
- 2Remove it from Git history using git-filter-repo or BFG.
- 3Force push the cleaned history.
- 4Check access logs on the affected service for unauthorized activity.
- 5Notify your team. If it is a shared credential, everyone needs the new key.
- 6Set up prevention (pre-commit hooks, secret scanning) so it does not happen again.
The first 5 minutes after discovery are critical. The longer an exposed key stays active, the more damage can be done.
Quick Reference
- •Generate SSH key: ssh-keygen -t ed25519 -C "email"
- •Test SSH connection: ssh -T git@github.com
- •Create .env.example: cp .env .env.example, then replace values with placeholders
- •Search for secrets in history: git log --all -p -S "secret_string"
- •Block secret commits: git secrets --install && git secrets --register-aws
Security is not about being paranoid. It is about building habits that protect you and your users by default. The five minutes it takes to set up proper key management today saves you from the five-alarm fire of a credential leak tomorrow.
For more developer guides and free tools, check out our blog and explore our developer tools.