Need help with Cryptex?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

TomFrost
131 Stars 17 Forks ISC License 56 Commits 13 Opened issues

Description

Secure secret storage and cryptographic key retrieval for Node.js

Services available

!
?

Need anything else?

Contributors list

No Data

Cryptex Build Status

Secure secret storage and cryptographic key retrieval for Node.js

var cryptex = require('cryptex')

cryptex.getSecret('mySQLPass').then(function(pass) { conn = mysql.connect({ username: user, password: pass, host: hostname }) })

Keep your secrets secret!

If you check database passwords into git, download credential files from S3 or some other server, provide plaintext keys to your continuous integration/deployment solution, or don't have the ability to limit engineers from getting production secrets, stop doing what you're doing.

Cryptex is here to help. Here's how:

1. Pick a configuration method

There are three ways to set up Cryptex. Use what works best for your project:

cryptex.json in your app root

Set up cryptex in different ways automatically depending on what the

NODE_ENV
environment variable is set to. The file looks like this:
{
  "production": {
    "keySource": "kms",
    "keySourceOpts": {
      "dataKey": "kms+encrypted+base64+string=="
    }
  },
  "development": {
    "keySource": "none",
    "algorithm": "plaintext",
    "secretEncoding": "utf8",
    "secrets": {
      "mySQLPass": "devlocal"
    }
  }
}

Put it right in the code

Don't want clutter in your file tree? That's cool. Do this:

cryptex.use({
  config: {
    keySource: 'kms',
    keySourceOpts: {
      dataKey: 'kms+encrypted+base64+string=='
    }
  }
})

Throw it all in environment variables

Following 12 Factor? Rock on. We have env var support already built-in.

CRYPTEX_KEYSOURCE=kms
CRYPTEX_KEYSOURCE_KMS_DATAKEY="kms+encrypted+base64+string=="

2. Pick a key source

Cryptex encrypts all of your secrets with a key. You don't want that key shared with anyone in plaintext, no matter how much you trust them. Cryptex will request your key when it's needed and delete it from RAM afterward. Here are the available sources:

Amazon KMS ("kms")

Amazon Web Services' Key Management System is the most secure and easy-to-implement key source. If you already have an AWS account, KMS is cheap and easy to use. Create an encryption key using the IAM console, and note the alias you gave it. Install the AWS CLI tool and run this command with your key alias to get an encrypted AES256 key:

aws kms generate-data-key-without-plaintext \
  --key-id alias/YOUR_KEY_ALIAS \
  --key-spec AES_256 \
  --output text \
  --query CiphertextBlob
kms options:

dataKey
CRYPTEX_KEYSOURCE_KMS_PATH
: The base64 string you got when you ran that command above.
region
CRYPTEX_KEYSOURCE_KMS_REGION
: The AWS region (such as us-east-1) in which the master KMS key can be found. If not specified, the config already loaded into aws-sdk is used.

A note about aws-sdk configuration: The KMS keySource uses Amazon's official Node.js aws-sdk library. If you're using

npm>=3
, it will use the same object as any you might have in your local project, carrying over the configuration. Otherwise, please see Amazon's guide on configuring the SDK to provide it with credentials. The highly recommended way to allow it to access KMS in production (assuming it's in production on AWS servers) is to attach an IAM role to the EC2 node with permission to access the master key you're using.

Load from file ("file")

If your secure key is available in a file, use this method. Note, however, that it is your responsibility to make sure that key file stays secure and inaccessible to prying eyes!

Is your key file something other than binary-encoded? Set

keySourceEncoding
in your config, or set the
CRYPTEX_KEYSOURCEENCODING
environment variable, to either
base64
or
hex
.
file options:

path
CRYPTEX_KEYSOURCE_FILE_PATH
: The path to the key file

Download via http(s) ("http")

DANGER. ONLY USE THIS IF YOU ABSOLUTELY KNOW WHAT YOU'RE DOING.

If you're using anything other than an https URL in production, you're definitely doing it wrong. You'll need to be an expert in locking your key server down for this to be anywhere near secure.

As with

file
, if your key file is something other than binary-encoded, set
keySourceEncoding
in your config, or set the
CRYPTEX_KEYSOURCEENCODING
environment variable, to either
base64
or
hex
.
http options:

url
CRYPTEX_KEYSOURCE_HTTP_URL
: The URL to the key file to download
timeout
CRYPTEX_KEYSOURCE_HTTP_TIMEOUT
: The number of milliseconds after which to fail the download. (Default: 4000)

Plain text ("plaintext")

DANGER. SHOULD NEVER BE USED IN PRODUCTION.

Useful for local development and testing, this allows the key to be saved in plain text. You'll also want to set

keySourceEncoding
in your config (or the
CRYPTEX_KEYSOURCEENCODING
environment variable) to either
base64
or
hex
-- however you've stringified your key.
plaintext options:

key
CRYPTEX_KEYSOURCE_PLAINTEXT_KEY
: Your key, in plain text

No key ("none")

This is useful if you're plugging in an algorithm that doesn't require a pre-set key to be used.

3. Pick an encryption algorithm

The recommended and default algorithm is

aes256
. If you're good with that, move on! You don't even need to set
algorithm
in your config or the
CRYPTEX_ALGORITHM
environment variable. But for the sake of completeness:

AES 256-bit ("aes256")

Military-grade symmetric encryption. The implementation in Cryptex computes a new random 128-bit initialization vector for each encrypted secret. Obviously, to use this, the key provided by your keySource must be a 256-bit AES key.

Plain text ("plaintext")

DANGER. SHOULD NEVER BE USED IN PRODUCTION.

Useful for local development. With this, no keySource is needed and all secrets can be stored in plain text. Remember to set

secretEncoding
in your config, or the
CRYPTEX_SECRETENCODING
environment variable, to
utf8
.

4. Secure your secrets

If you installed Cryptex globally, you'll have a CLI tool called

cryptex
that can encrypt and decrypt your keys according to your
cryptex.json
or environment variables. It's this easy:
$ cryptex encrypt mypassword
Q+JfrQS5DtSjqWHu1oO4HqctA2hVw4VhaDQfBCuvO8U=

To specify a particular node environment (for

cryptex.json
users), pass it in the
-e
flag. Run
cryptex --help
for all the details.

5. Save your secrets

Provide your secrets to production by either putting them in your config like this...

// cryptex.json
{
"production": {
  "keySource": "kms",
  "keySourceOpts": {
    "dataKey": "kms+encrypted+base64+string=="
  },
  "secrets": {
    "mySQLPass": "Q+JfrQS5DtSjqWHu1oO4HqctA2hVw4VhaDQfBCuvO8U="
  }
}

...or by saving them in uppercase environment variables prefixed by

CRYPTEX_SECRET_
:
CRYPTEX_SECRET_MYSQLPASS="Q+JfrQS5DtSjqWHu1oO4HqctA2hVw4VhaDQfBCuvO8U="

API

First, grab an instance:

var cryptex = require('cryptex')
new cryptex.Cryptex(opts = {})

Creates a new Cryptex instance with the specified options. See the

update
function below for an option list.
cryptex.encrypt(data: string|Buffer, encoding = "utf8"): string

Encrypts the given data into a base64 string. If your string is binary data encoded as base64 or hex, just pass

base64
or
hex
for the encoding. The encoding is ignored if a Buffer is passed in.
cryptex.decrypt(data: string|Buffer, encoding = "base64"): string

Decrypts the given data and passes it back as utf8. If your string is binary data encoded as base64 or hex, just pass

base64
or
hex
for the encoding. The encoding is ignored if a Buffer is passed in.
cryptex.getSecret(secret: string, optional = false): Promise<string>|null

Gets a Promise that resolves to a pre-saved secret, decrypted. See step 5 above. If no secret of the given name was found, this function with reject by default. To have it resolve with null instead, set

optional
to true.
cryptex.getSecrets(secrets: Array<string>, optional = false): Promise<Object>

Gets a Promise that resolves to an object mapping the requested secret names to their decrypted values. Unlike calling

getSecret
multiple times, this function will wait for the key to be cached (if enabled) from decrypting the first secret before decrypting the rest. This provides a faster and more efficient delivery of multiple secrets. If
optional
is set to
true
, any requested secrets not found will have their values in the object map set to
null
. Otherwise, the returned Promise will be rejected.
cryptex.update(opts = {})

Updates the cryptex instance with new configuration. Available options are:

file: The path to a json file to load, mapping environments names (as pulled from

$NODE_ENV
) to configuration objects. Can also be set in
CRYPTEX_FILE
. Defaults to
cryptex.json
in the app process's current working directory.

env: The environment to select from the specified json file. Cryptex will attempt to pull an environment in this order: This value, the

CRYPTEX_ENV
env var, the
NODE_ENV
env var, or default to
default
if all else has failed.

cacheKey: Boolean true to cache the key returned by the keySource in RAM, false to pull the key from the source every time it's needed. Can also be set in

CRYPTEX_CACHEKEY
with "true" or "false". (Default: true)

cacheTimeout: If cacheKey is true, the number of milliseconds after which to delete the key and allow the Node.js garbage collector to remove it from RAM. Set to 0 to disable the timeout (NOT RECOMMENDED). Can also be set in

CRYPTEX_CACHETIMEOUT
. (Default: 5000)

config: A configuration object specifying the keySource and other information outlined above. If this is set, Cryptex will not attempt to load a configuration file, and the

environment
setting is ignored. Set config to
{}
to force all configuration to be set in environment variables.

Installation

Get it global and local for the super convenient command-line tool:

npm install -g cryptex
npm install --save cryptex

Dependencies

Cryptex uses ES6 native Promises, available in Node.js version 0.12 and up.

Security warning

The state of cryptographic security in Javascript is abysmal at best. Node lends itself to far better possibilities than what you'd find in the browser, but be aware of the following types of attacks:

  • Javascript modules that require('cryptex') or some other portion of your code, overriding
    getSecret
    or other functions to steal your decrypted private data.
  • Javascript modules that require('crypto') and monkey-patch the built-in Node library to steal private data.
  • Nefarious applications that dump the RAM from your Node process. Node's garbage collector cannot be forced to run from Javascript, so even turning off the key cache could expose a window in which an unencrypted key could be stolen.
  • Applications, server users, or javascript modules that read local key files. When in doubt, use Amazon KMS.

This article by NCC Group is from 2011 and focuses on the security of Javascript in the browser, but is still very much applicable today. However, by using Amazon KMS, Cryptex, and with careful review of all installed modules, a secure system in Node.js is possible.

Versions

Embassy supports Node 4 LTE and higher out of the box. For 0.12, consider compiling with Babel.

License

Cryptex is released under the ultra-permissive ISC license. See LICENSE.txt for details.

Credits

Cryptex was originally created at TechnologyAdvice in Nashville, TN.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.