What's the best way to handle configurations or secrets like API keys with Node.js? One simple way is to use environment variables.
You could pass them directly to your node command or add them to the package.json. Let's have an index.js
, that looks like
const apiKey = process.env.API_KEY
console.log(apiKey)
You can, for example, start your application with
API_KEY=super-secret-key node index.js
You can also put the same into your package.json
...
"scripts": {
"start": "API_KEY=super-secret-key node index.js"
},
...
and start your application with npm start
. That way you at least don't have to type your API key every time you start your application.
The problem with this approach is, that you have to commit your package.json to your repository. But you should not share secret keys like this. So there's a better way to do it: using a .env
file.
So you can add your API key to this .env
file and consume it, like before, with the dotenv
library.
run
npm install dotenv
to install the library.
Then import and use it like this in your application:
require('dotenv').config();
const apiKey = process.env.API_KEY
console.log(apiKey)
Your '.env` file will now contain your secret.
API_KEY=super-secret-key
Ideally, you would then also create an entry in your .gitignore
to exclude your .env
file from version control.
Put this in the .gitignore
:
.env
This way you have all your secrets in one place and you don't accidentally leak any secrets.
Top comments (3)
@benjaminmock, thanks for taking the time to share with us! This comment runs contrary to the advice in your post. It is not intended to judge you or discourage you from sharing with the community. It is only intended to educate on the not-discussed-enough risks of secrets in environment variables. β€οΈ
Environment variables are a popular and convenient way to configure an application and I encourage their use. But don't use them for your secrets.
The idea of using ENV for secrets has become prolific in the last few years. I see this recommendation, a lot. Maybe it is because of the quotability of "The Twelve-Factor App" which encourages ENV as the primary vehicle for configuration... or maybe it is because ENV is so convenient when it comes to containerized apps. But it is not good practice.
(A quick side-note, The Twelve-Factor App does not address secrets one way or another so it is not fair to say the author of that essay popularized the idea. I'm convinced that it added to the popularization of config-by-ENV. From there, I think people just made the leap to secrets because "secrets" have historically be considered part of "configuration.)
Why is this bad practice? Simply put, your secrets become globally available to the application (part of what makes it convenient!)... that means every package you bring in, directly or indirectly has access trivial access to your secret. A well-meaning package could simply output the entirety of the ENV in an error case to help with problem-identification and now you unexpectedly have secrets sitting in a log somewhere. A nefarious package could capture the entire ENV in search of secrets, perhaps even looking for specific key phrases in the env variables name.
Many application also spawn child processes. Unless you are careful about it, your ENV becomes the env of the child process. This inheritance behavior is fundamental to the env. Some security-minded programs will include mechanisms to prevent this inheritance (
sudo
does this) but most do not -- mainly because env inheritance is expected. But not for secrets. Try the following.See points above about well-meaning, and bad-intentioned libs and extend that to other processes you may spawn.
Instead, put your secrets in a file and secure that file using appropriate access control. Load that file when you need the secrets.
At the end of the day, my advice is: Use environment variables for configuration. Do not use them for secrets.
P.S. Here's some other posts by not-me on this subject:
thank you Benjamin
Any thoughts on encrypting values in the dotenv file