Node.js: Managing Secrets

How to manage secrets with Node.js

👋 Welcome to the Stackhero documentation!

Stackhero offers a ready-to-use Node.js cloud solution that provides a host of benefits, including:

  • Deploy your application in seconds with a simple git push.
  • Use your own domain name and benefit from the automatic configuration of HTTPS certificates for enhanced security.
  • Enjoy peace of mind with automatic backups, one-click updates, and straightforward, transparent, and predictable pricing.
  • Get optimal performance and robust security thanks to a private and dedicated VM.

Save time and simplify your life: it only takes 5 minutes to try Stackhero's Node.js cloud hosting solution!

When your Node.js project interacts with a database, object storage, or an external API, securely storing credentials such as usernames, passwords, or tokens, often referred to as "secrets", is crucial. Keeping these secrets confidential is essential for maintaining security.

You might initially consider something like this:

// Connecting to a PostgreSQL database
const pg = new Client({
  host: 'xxxxx.stackhero-network.com',
  user: 'admin',
  password: 'myPassword',
  database: 'admin'
});

Unfortunately, this method is not secure. Why? Because your secrets could end up in your Git repository, exposing them to anyone with access. Even if you think only you have access, it is akin to leaving a Post-it with your passwords on your monitor and assuming no one else will see it. This can eventually lead to security issues.

Additionally, this approach does not support seamless management of different environments, such as development and production.

A widely adopted industry practice is to store secrets in environment variables.

Environment variables are defined outside your code and set before Node.js starts. The concept is to define all your secrets using environment variables, avoiding hard-coding them into your application.

To define an environment variable, you can set it at the start of your Node.js invocation like this: MY_PASSWORD=myDevelopmentPassword node app.js

This command sets a variable named "MY_PASSWORD" with the value "myDevelopmentPassword". The format is simply <KEY>=<value>.

By convention, environment variables are written in capital letters. Note that only strings can be utilized, not arrays or objects.

In your app.js file, you can read your new environment variable with process.env like this: console.log(process.env.MY_PASSWORD);. This will display myDevelopmentPassword.

Now, the password is defined outside the code, preventing it from being published to the Git repository.

On Stackhero, in a production environment, you have the option to define a new environment variable named "MY_PASSWORD" containing "myProductionPassword" directly in your Node.js service dashboard, ensuring smooth operation.

Example of Node.js configuration on the Stackhero dashboardExample of Node.js configuration on the Stackhero dashboard

In conclusion, your password is no longer stored in your code, and you have separate passwords for development and production environments.

In real-world scenarios, multiple secrets often need to be managed. For instance, connecting to a database requires storing a hostname, a user, and a password.

Handling a single secret is straightforward, but managing several can become cumbersome. Imagine starting your application with this command line:

POSTGRESQL_HOST=xxxxx.stackhero-network.com POSTGRESQL_USER=admin POSTGRESQL_PASSWORD=myPassword node app.js

This line is unwieldy and challenging to maintain. A production application could require multiple variables, making this approach impractical.

That is where the dotenv library becomes invaluable.

With dotenv, secrets can be stored in a separate file called .env.

First, you can install the dotenv library with:

npm install dotenv

Then, create a .env file containing the variables:

POSTGRESQL_HOST=xxxxx.stackhero-network.com
POSTGRESQL_USER=admin
POSTGRESQL_PASSWORD=myPassword

Ensure this .env file is not pushed to the Git repository by adding it to the list of ignored files:

echo ".env" >> .gitignore

Finally, load the dotenv library at the top of your app.js file like this:

require("dotenv").config();

With this configuration, when starting your application with node app.js, dotenv will automatically read the .env file contents on your development platform. In production, this file does not exist, and environment variables are sourced directly from your Node.js service configuration (available on the Stackhero dashboard).

All of this was theory. Let us dive into a real working example.

You can find a complete example available here: https://github.com/stackhero-io/dotenvWithNodejs

Now you know how to handle your secrets in a flexible and secure way, all very easily.