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, it is essential to store credentials—such as usernames, passwords, or tokens—securely. These credentials are often referred to as "secrets." Protecting your secrets is crucial for ensuring the security of your application.

At first, you might consider writing your credentials directly in your code, like this:

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

However, this method is not secure. Your secrets could end up in your Git repository, making them accessible to anyone with access to the code. Even if you think you are the only one with access, it is a bit like leaving a Post-it note with your passwords on your monitor and hoping no one notices. This can eventually lead to serious security issues.

Additionally, hard-coding secrets makes it difficult to manage different environments, such as development and production.

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

Environment variables are defined outside your code and are set before Node.js starts. The idea is to define all your secrets using environment variables, so they are never hard-coded in your application.

You can define an environment variable by passing it at the start of your Node.js command, like this:

MY_PASSWORD=myDevelopmentPassword node app.js

This command creates a variable called MY_PASSWORD with the value myDevelopmentPassword. The format is straightforward: <KEY>=<value>.

By convention, environment variables are written in uppercase letters. Note that environment variables can only contain strings, not arrays or objects.

In your app.js file, you can access your environment variable via process.env, like this:

console.log(process.env.MY_PASSWORD);

This will display myDevelopmentPassword.

Now, your password is defined outside your code. This helps prevent it from accidentally ending up in your Git repository.

If you are using Stackhero in a production environment, you can define a new environment variable named MY_PASSWORD with the value myProductionPassword directly from your Node.js service dashboard. This makes it easy to manage different environments smoothly.

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

With this approach, your password is no longer stored in your code, and you can easily use different credentials for development and production environments.

In real-world projects, you often need to manage multiple secrets. For example, connecting to a database usually requires a hostname, username, and password.

Managing a single secret is simple, but juggling several quickly becomes cumbersome. Imagine starting your application with a command like this:

POSTGRESQL_HOST=<XXXXXX>.stackhero-network.com POSTGRESQL_USER=admin POSTGRESQL_PASSWORD=myPassword node app.js

This quickly becomes hard to read and maintain. In production, you will likely have even more variables, making this approach impractical.

That is where the dotenv library comes in.

With dotenv, you can store your secrets in a separate file called .env.

To get started, install the dotenv library by running:

npm install dotenv

Next, create a .env file to store your variables:

POSTGRESQL_HOST=<XXXXXX>.stackhero-network.com
POSTGRESQL_USER=admin
POSTGRESQL_PASSWORD=myPassword

To keep your secrets secure, make sure your .env file is not added to your Git repository. You can do this by adding it to your .gitignore file:

echo ".env" >> .gitignore

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

require("dotenv").config();

With this setup, when you start your application using node app.js, dotenv automatically reads your .env file on your development machine. In production, you do not need the .env file. Environment variables are pulled directly from your Node.js service configuration, which you can manage through the Stackhero dashboard.

Now that we have covered the theory, let us look at a concrete example.

You can find a complete, working sample here: https://github.com/stackhero-io/dotenvWithNodejs

With these techniques, you can manage your secrets in a flexible and secure way, while keeping the process simple and efficient.