Secure Your Secrets with .env
Published: 2024-01-09 17:00:52

Using environment variables to store secrets instead of writing them directly into your code is one of the quickest and easiest ways to add a layer of protection to your projects. There are many ways to use them, but a properly utilized `.env` file is one of the best, and I’ll explain why.

## They’re project scoped
Environment variables are a part of every major operating system: Windows, MacOS, and all the flavors of *nix (Unix, BSD, Linux, etc.). They can be set at an operating system level, user level, session level… It gets complicated, and where/how you define them matters to the scope in which they can be accessed.

This variety of scopes also creates the distinct possibility of variable collisions. If you’re looking for an environment variable named `API_KEY`, that could be getting re-defined in each scope, and if you’re not steeped in that OS, it’s extra work to be sure you’re not clobbering something someone set at a different scope that some other app or service needs.

`.env` files are only consumed at runtime and only in the context of the app that’s consuming them. That prevents them from clobbering any other environment variables on the system that might be consumed outside your app.

## They can be "ignored"
If you’re working on a JavaScript application in Node, you can’t ignore your `index.js` file in the version control system. It contains essential code. But you can [set your `.gitignore` file](https://youtu.be/3KrjEytC5qc) to have the Git system ignore your `.env` file. If you do that from the inception of your repository, you won’t commit secrets to the project’s Git history.

A better option is to include a `.sample.env` file that sets the variable names, but only includes dummy data or blanks. People cloning/forking and using the repository can get the secrets via another route, then `cp .sample.env .env` (in a terminal), and assign the real values to the proper variables in the ignored `.env` file.

## They’re relocatable
While most systems will default to looking for the `.env` file in the root of the app’s primary directory, you can always have it a level or two higher. So, if for example, a server configuration error or code bug leaves it possible to view all the files at the root of your web app as a directory, the `.env` will not be there for easy pickings.

This is not an uncommon practice. SSH keys are stored by default at `~/.ssh` (a "hidden" subdirectory of the home directory of the user) on Windows, Mac, and Linux. You do not need to move them into the root directory of a project that uses them.

## A quick .env demo in Node
Let’s say your working directory for the app you’re building is `~/Documents/work/projects/games/tictactoe` and `tictactoe` is the root directory for the app you’re building and your Git repository. You can put the `.env` in the next directory up, `games`. And while we generally call the file type `.env`, you can call it `.toecreds` if you want to make it a distinct file that other processes would never even think to touch. We'll use that in the demo.

**Here’s how you’d do that in Node.js.:**

1\. In your `games/tictactoe` directory, `npm init` (go with the defaults) and then `npm install dotenv`.

2\. Create your `.toecreds` file in the `games` directory.

3\. Fill the .toecreds file with information in the following format `VARIABLE_NAME=VALUE` (no spaces). You can also start a line with # for a comment.
 ```shell
# Leaderboard SaaS
LEADERBOARD_ENDPOINT=https://example.com/leaderboard/v1
LEADERBOARD_KEY=jknfwgfprgmerg…
```
4\. At the top of your `index.js` (or whatever file is your launchpoint) in `games/tictactoe`, include the following lines:
```javascript
require('dotenv').config({ path: '../.toecreds' })
console.log(process.env.LEADERBOARD_ENDPOINT)
```

Run your `index.js` and the endpoint URL will be output to the terminal. Meanwhile, the environment variables you set in it will not be available from the terminal and the file lives a level above your repository and can't accidentally be swept up if you misconfigure `.gitignore`. 

Try adding a long timeout to the script and then running `node index.js &` to return control back to the terminal after invoking the script. While the script is running in that shell session, the environment variables available to the shell still do not contain the secrets. They are scoped to your running application.

You can have dev, test, and prod credential sets, having your CI/CD tooling pull the correct keys for the deployment target from a secrets manager and write the `.toecreds` (or `.env`) file to the same relative directory.

## And there you have it
The use of a `.env` file helps you keep your app's secrets from ever being committed to your version control and provides an additional layer of protection against your secrets being discovered by hackers or other prying eyes. It's a great addition to your developer / dev-ops toolbox.