@tka85/dotenvenc
Version:
Encrypt and decrypt in-memory at runtime your .env file so you can store sensitive information (passwords etc.) in source control
235 lines (153 loc) • 8.67 kB
Markdown
# @tka85/dotenvenc
**NOTE**: *This is an improved version of the now deprecated [dotenvenc](https://www.npmjs.com/package/dotenvenc).*
Are you using `.env` and it contains secrets like passwords & tokens?
And are you using [`dotenv`](https://www.npmjs.com/package/dotenv) to expose those secrets as `process.env` variables to your app?
**Problem**: you are exposing the secrets in plain text in your repository and your production system.
**Solution**: you can now save your `.env` encrypted as `.env.enc`, then decrypt it during runtime only in memory (never on disk) and transparently get the same functionality as you enjoyed from `dotenv`.
## Benefits
* Secure!
✔ You can safely commit into your codebase the encrypted `.env.enc` without compromosing your secrets
* Secure!!
✔ Your secrets exist unencrypted only in memory during runtime; never on disk
* Secure!!!
✔ Secrets protected with strong encryption (AES-256)
* Handy!
✔ Comes with CLI script `dotenvenc` for easily updating your `.env.enc` from your local (uncommitted) `.env`
* Lean!
✔ Still maintain a single dependency but now on `dotenvenc` instead of `dotenv`
* Easy!
✔ Transition from `dotenv` replacing a single line of code
* Flexible!
✔ Not limited to `.env` and `.env.enc` i.e. you can set any custom filenames for the encrypted and unencrypted files
* Modern!
✔ Fully re-written in Typescript
## Tip
Add `.env` in your `.gitignore` so your unencrypted secrets are guaranteed to never get committed in your codebase.
## New feature in v5.4.1
You can run commands in silent mode passing `-s` or `--silent`. In this case they will not print any informative or warning messages. Only the expected output (decrypted data) and errors.
## New feature in v5.2.0
Each time you encrypt your `.env` now you select (`-r` parameter) to get additionally to the `.env.enc` also a `.env.enc.readable` file (or if you are using a custom env file, the same named with a `.enc.readable` suffix). This extra `.readable` file contains the variable names in human readable form, and the values as HMAC digests. How is that useful? It serves so you can see each time you re-generate the encrypted `.env.enc`, which entreis have been changed.
### CAUTION
If this `.readable` file is committed to a public repository, it exposes you to the danger that, even though HMAC is a cryptographic hash function which is designed to be difficult to reverse, an attacker might attempt a brute force or dictionary attack and expose your secrets.
## Installation
```bash
npm i @tka85/dotenvenc
```
## Encryption
You have a `.env` (or custom-named unencrypted secrets file) and you will generate a `.env.enc` (or custom-named file) which is encrypted and safe to commit.
You can use the handy command-line script `dotenvenc` that comes installed with the package. Just run it without arguments to see the help page.
### Step 1 (optional)
#### Convenient option
Save the encryption/decryption password you will be using in the environment variable `DOTENVENC_PASS` in your `.bashrc` (or `.bash_profile`):
```bash
export DOTENVENC_PASS='mySuperDuperPassword';
```
and reload it:
```bash
source ~/.bashrc
```
Upon runtime your app will use this env variable when reading the encrypted `.env.enc` to decrypt it and populate your `process.env` (see following section `Decryption` on how to do this).
But setting this env variable is also helpful for the CLI tool `dotenvenc`. If `DOTENVENC_PASS` is set, the `dotenvenc` script will not prompt you each time to type the password for encryption/decryption.
#### Secure option
For maximum security, do not save the `DOTENVENC_PASS` not even as an environment variable in your `.bashrc`. If it is not set, the application will ask for it upon startup before it can proceed to decrypt `.env.enc` and populate your `process.enc`.
### Step 2: encrypt .env
Note: you will have to repeat this step each time you make changes to a secret in your unencrypted `.env` and need to reflect it into the encrypted `.env.enc`.
If your unencrypted secrets file is `.env` and resides at the root of the project, then simply:
```bash
./node_modules/.bin/dotenvenc -e
```
will prompt you (x2) for an encryption password (unless `DOTENVENC_PASS` is set) and proceed to generate an encrypted secrets file `.env.enc`.
Optionally you can also generate the semi-readable `.readable` file passing `-r` additionally to `-e`:
```bash
./node_modules/.bin/dotenvenc -e
```
And if your unencrypted secrets file is not named the default `.env`, we have you covered:
```bash
./node_modules/.bin/dotenvenc -e -i /path/to/my/secrets-env-filename
```
will prompt you (x2) for an encryption password (unless `DOTENVENC_PASS` is set) and proceed to generate an encrypted secrets file `.env.enc`.
And if you don't want to name the encrypted secrets file `.env.enc`, we also have you covered:
```bash
./node_modules/.bin/dotenvenc -e -i /path/to/my/secrets-env-filename -o /another/place/to/my/encrypted-secrets-env-filename
```
will prompt you (x2) for an encryption password (unless `DOTENVENC_PASS` is set) and proceed to generate an encrypted secrets file `/another/place/to/my/encrypted-secrets-env-filename`.
## Decryption
Let's assume the contents of the `.env` that you encrypted into `.env.enc` are:
```text
DB_PASS='superDuperPassword'
SECRET_TOKEN='noMoreSecrets'
```
For all possible decryption scenarios that follow, the principle is:
* If you have set env var `DOTENVENC_PASS`, no additional step is needed
* If you have not set env var `DOTENVENC_PASS`, you will be prompted to supply the decryption password before proceeding
You can now populate the `process.env` in your app's code as follows:
```javascript
require('dotenvenc').decrypt();
// From here on your app will have access to the secrets through `process.env.DB_PASS` and `process.env.SECRET_TOKEN`
```
or in ES6:
```ES6
import { decrypt } from 'dotenvenc';
decrypt();
// From here on your app will have access to the secrets through `process.env.DB_PASS` and `process.env.SECRET_TOKEN`
```
If you used a custom encrypted filename:
```javascript
require('dotenvenc').decrypt({ encryptedFile: './somewhere/.secrets.custom.enc'});
// From here on your app will have access to the secrets through `process.env.DB_PASS` and `process.env.SECRET_TOKEN`
```
or in ES6:
```javascript
import { decrypt } from 'dotenvenc';
decrypt({ encryptedFile: './somewhere/.secrets.custom.enc'});
// From here on your app will have access to the secrets through `process.env.DB_PASS` and `process.env.SECRET_TOKEN`
```
## Recovery of unencrypted secrets file
You want to decrypt and view the contents of your encrypted secrets file?
A new team member wants to recreate the `.env` upon checkout of the project? (remember that `.env` is an unversioned file)
Or you want to recreate the `.env` because it got lost or corrupted?
You can use the same handy command-line `dotenvenc` you used to initially encrypt your unencrypted `.env` secrets file.
Using the script's `-d` flag:
```bash
./node_modules/.bin/dotenvenc -d
```
or if you used custom name instead of the default `.env.enc`:
```bash
./node_modules/.bin/dotenvenc -d -i ./somewhere/.secrets.custom.enc
```
In both cases you will be prompted to provide the password that was used to create the encrypted file (if `DOTENVENC_PASS` is not set) and, assuming the password is correct, you will have the contents printed on the screen in the format:
```
VAR1=VALUE1
VAR2=VALUE2
...
```
## Bonus
You can dump the contents of your encrypted secrets file as shell `export` statements.
Using the script's `-x` flag:
```bash
./node_modules/.bin/dotenvenc -x -i ./somewhere/.secrets.custom.enc
```
This will print on console:
```
export VAR1=VALUE1;
export VAR2=VALUE2;
```
### How is that useful?
Using the shell's `eval` in a shell script you can populate your environment dynamically without ever storing the sensitive information on disk.
```
eval `./node_modules/.bin/dotenvenc -x`
# all commands following in the script will now have access to the env variables
```
## Comments
Anything following a `#` sign in the `.env` file, is stripped. That means you can have full line comments:
```
# the whole line is a comments because it start with a "#"
```
or inline comments:
```
VAR="a value" # text before the "#" is kept; text following the "#" is stripped
```
## Inspired by
* [Keeping passwords in source control](http://ejohn.org/blog/keeping-passwords-in-source-control/)
* [envenc](https://www.npmjs.com/package/envenc)
* Based on the now deprecated [dotenvenc](https://www.npmjs.com/package/dotenvenc)