Practicing secret hygiene
Ever since I accidentally pushed a set of credentials to a branch on GitHub, and frantically tried to santizize the repo late on a Friday, I’ve adopted a couple of practices to prevent this from happening again. Here’s what has been working really well for me.
Prefer short-term access tokens over long-term credentials
When possible, I now avoid storing long-term credentials locally. For example, AWS credentials commonly stored in ~/.aws/credentials
can be replaced by short-term credentials (see gimme-aws-creds
and aws sso login
for good examples of ways to accomplish this).
Use a global .gitignore
to ignore files you commonly use to store credentials
If I do store plaintext credentials, a good fallback to make sure I don’t accidentally commit and push them to a repo is to ignore files containing credentials in a global .gitignore
file.
# ~/global.gitignore
**/.env
This ignores .env
files globally so they never show up in your git working tree. This step is fairly simple to implement given it does not require an external program other than git
.
Use a password manager to dynamically inject your secrets as environment variables
1Password works really well here. Here’s an example. Let’s say my application needs a set of environment variables and I’d like to store them in a .env
file like so:
POSTGRES_HOST=127.0.0.1
POSTGRES_USER=admin
POSTGRES_PASSWORD=somepassword
I instead replace the plaintext secrets with 1Password Secret References and have 1Password inject these as environment variables. Here’s how I would edit the .env:
POSTGRES_HOST=127.0.0.1
POSTGRES_USER=admin
POSTGRES_PASSWORD="op://Private/Postgres_Password"
And invoke my application with this:
op run --env-file="./.env" -- node app.js
This calls the 1Password CLI which injects all three secrets in my .env
as environment variables that my application can read from.
Using a handy alias, I made this command a little less unwieldy:
# ~/.zshrc
opr() {
# opr .env <command> [flags]
op run --env-file "$1" -- "${@:2}"
}
To invoke my application with secrets injected, I’d run this:
opr .env node app.js
By replacing my plaintext passwords with 1Password secret references, I’ve reduced a risk factor and a lot of potential headache.
Though 1Password has worked really well for me, some alternatives to consider might be BitWarden (personal password manager), Hashicorp Vault, and AWS Secrets Manager (enterprise-grade secret managers).
Closing thoughts
Practicing secret hygiene individually is a good thing, no doubt. But it helps to invest in tooling that detects and removing plaintext secrets at scale. These tools typically integrate within source control, CI/CD, and IaC tooling to help identify exposed secrets. I’ve also found the OWASP Cheat Sheet Series to be a great resource for all things application security.