npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

encryptio

v0.0.5

Published

Lightweight, secure and straightforward encryption & decryption for Node.js: 30 LOC,

Downloads

5

Readme

npm bundle size

Simple, yet secure, encryption / decryption using Javascript.

  • ZERO dependencies: uses crypto which natively ships with Node.js without any further install
  • Tree-shakeable: import only functions you need.
  • Random: Encrypted strings are always different ( using Initialization Vector )
  • Asynchronous or Synchronous: Choose between promise-based (.then() or async/await) or Sync
  • Options: support manual algorithms and encodings (see the Options section)

Quick start

npm install encryptio

Asynchronous (default):

var encrypt = require('encryptio').encrypt
var decrypt = require('encryptio').decrypt
// or using ES6:
import {encrypt, decrypt} from 'encryptio'

var MY_SECRET = process.env.SECRET_KEY // See note on "storing the secret key"
                                       // secret_key should be 32 characters for AES256

var main = async function(){
    var clearText = 'Hello World'
    console.log('Original string: ', clearText)

    var encText = await encrypt(MY_SECRET, clearText)    // Note thay encrypt() outputs
                                                         // an array as follow:
                                                         // [IV, String, Auth]
    console.log('Encrypted string: ', encText.join('.')) 


    var decText = await encrypt(MY_SECRET, encText)      // Note that decrypt() needs
                                                         // an array as follow
                                                         // [IV, String, Auth]
    console.log('Encrypted string: ', decText)
}

main()

// > prints:
// Original string: Hello World
// Encrypted string: 5lSto7M4riJKmorPvrToBQ==.qmZZr48DEDYARyQ=.okPYfpnuBj24UDMrdKEYKQ==
// Decrypted string: Hello World

Synchronous

var encrypt = require('encryptio').encrypt
var decrypt = require('encryptio').decrypt
// or using ES6:
import {encryptSync, decryptSync} from 'encryptio'

var MY_SECRET = process.env.SECRET_KEY // See note on "storing the secret key"
                                       // secret_key should be 32 characters for AES256

var clearText = 'Hello World'
console.log('Original string: ', clearText)

var encText = encryptSync(MY_SECRET, clearText)
console.log('Encrypted string: ', encText)

var decText = decryptSync(MY_SECRET, encText)
console.log('Encrypted string: ', decText)

// > prints:
// Original string: Hello World
// Encrypted string: 5lSto7M4riJKmorPvrToBQ==.qmZZr48DEDYARyQ=.okPYfpnuBj24UDMrdKEYKQ==
// Decrypted string: Hello World

Why encrypt() outputs 3 things in an array ?

Well, encrypt() and encryptSync() output an array in the form of:

let [IV, EncString, Auth] = encrypt(process.env.SECRET_KEY, 'Hello')

The same array is expected to be provided to decrypt() and decryptSync():

decrypt(process.env.SECRET_KEY, [IV, EncString, Auth])

Here is what they represent:

  • IV is the Initialization Vector (=the seed). It is mandatory in GCM, CBC and CTR. Since the IV is changed at each encryption, the decrypt() function needs to know the seed to calculate the correct decryption.

  • EncString is the actual encrypted string.

  • Auth is the signature part for the authentication. It is output (and required) only if the algorithm ends by gcm (like the default aes-256-gcm). If non-auth algorithms are used, like aes-256-cbc for example, this Auth part is not output in encrypt() nor encryptSync(), and not required in decrypt() nor decryptSync().

Why the fuzz ?? Why not just concatenate everything off the bat in the library ?!

Short answer, for modularity.

Long answer: one can decide to apply further transformation to each part of the output. For example apply a salt. Another thing is how to store these parts: one could eventually store each part in separate stores. Finally, one could trim off some part of the IV and Auth string when using base64 encoding (default).

Options

By default, encryptio uses aes_256_gcm algorithm to encrypt strings and base64 to encode them.

Both options are manually configurable. Just pass an object to encrypt/decrypt functions like so:

var custom_options = {
    algorithm: 'aes-256-cbc',
    encoding: 'hex'
}

var main = async function(){
    const clearText = 'Hello World';
    const encText = await encrypt(process.env.SECRET_KEY, clearText, custom_options);
    await decrypt(process.env.SECRET_KEY, encText, custom_options);

    // or using Sync
    const encText = encryptSync(process.env.SECRET_KEY, clearText, custom_options);
    decryptSync(process.env.SECRET_KEY, encText, custom_options);
}

There are a lot of options to choose from when it comes to encryption. CBC, CTR and GCM are the most used and most secure. Actually, a lot of examples on tutorials use CBC, but this should not be the best practice to widespread. In fact, CBC is a strong encryption algorithm but it lacks authentication and should be used with an extra layer of authentication like HMAC or else... GCM has the advantage of combining both encryption + authentication, making it a better choice for most situations.

algorithm
  • aes-256-gcm (default) : requires a 32 byte secret key. Has Auth.
  • aes-256-ctr : requires a 32 byte secret key. No Auth.
  • aes-256-cbc : requires a 32 byte secret key. No Auth.
  • ... : there are a lot of other algorithms. Really, stick to one of those 3, or better: keep it to default aes-256-gcm.
encoding
  • base64 (default): output balanced size readable strings
  • hex: output readable strings larger than base64
  • binary: output compact strings but not readable (ok for inter-machine but cannot share with humans)

Note on storing the secret key

!! NEVER PUT YOUR SECRET KEY IN YOUR CODE !!

!! NEVER PUT YOUR SECRET KEY IN YOUR CODE !!

!! NEVER PUT YOUR SECRET KEY IN YOUR CODE !!

It is strongly recommended to store any private key or secret localy (on a secured machine...) and never send these to the cloud or even to git or even to team mates. Here are 3 recommended ways to store them:

Quick and dirty

Export a variable in your terminal before node:

SECRET_KEY=1234567890098765432112 node index.js

Then in your code (here index.js as an example), you can call your variable like this:

// index.js
// ...
var my_secret = process.env.SECRET_KEY
// ...

Using direnv (recommended)

direnv solves this problem quite nicely. First you install direnv (just go and follow the steps in direnv docs). Then, at the root of your project, you can create a file named .envrc and put your secrets like so:

# in your .envrc
export SECRET_KEY=1234567890098765432112

Then in your code (here index.js as an example), you can call your variable like this:

// in your index.js
// ...
var my_secret = process.env.SECRET_KEY
// ...

CAUTION: Please, make 100% sure that your .envrc is never sent to git or anywhere. It should just be kept secret in your machine. So, do not forget to update your .gitignore file accordingly:

# in your .gitignore
# exclude direnv file
.envrc

Using dotenv

dotenv does somewhat the same thing as direnv but has to be explicitly added to your code.

First create a .env file at the root of your project:

SECRET_KEY=1234567890098765432112

then install dotenv

npm i dotenv --save

finally in your code, import dotenv, and get your vars:

// in your index.js
require('dotenv').config()

var my_secret = process.env.SECRET_KEY

CAUTION: Please, make 100% sure that your .env is never sent to git or anywhere. It should just be kept secret in your machine. So, do not forget to update your .gitignore file accordingly:

# in your .gitignore
# exclude dotenv file
.env

How secure is this ?

Good read is this SO thread

DOs

You can safely use the lib to encrypt low to highly confidential things.

!! USE A REALLY RANDOM SECRET KEY !!

Just to recap, here are some common things adressed:

  • This is AES-256-GCM by default. One cool thing about it is that the encryption is not made by blocks of words but rather given the whole string. So it is not possible to statistically count word/letter frequencies against the encrypted string.

  • AES-256-GCM enforces a 32 bytes long secret key composed of any utf-8 characters. It would require billions of billions of years to try all possibilities of the secret key. That beinng said, if your secret key is 00000000000000000000000000000000 it may take only 2 minutes... since any brute-force program might just start with easy strings first (zeros, sequences, words dictionnaries...). So make it is VITAL to make your secret as random as possible !

  • AES-256-GCM combines encryption with a signature for authentication. This means that one first step is made to obfuscate the original string, but a second step is made to sign the encrypted string so that an attacker cannot forge the encrypted string. If an attacker attempts to modify the encrypted string (trying to insert/guess something new), the algorithm will not even try to decrypt the string: it will know the string has been corrupted.

  • Since encryptio uses a random Initilization Vector for each encryption, several encryption of the same string will always output a completely new encrypted string. This means that an attacker cannot compare two encrypted strings in the hope to find patterns.

Really, unless you pick a dumb easy secret-key and/or blindly distribute it to the world... you should be able to sleep peacefully. It would require a considerable amount of efforts and money to break this encryption.

DONTs

Let's face it, if you plan to use this lib to encrypt political, highly pricey secrets, or the name of who killed JFK... that may not be a good idea.

Like exposed in the DOs section above, this is not a bulletproof solution. The main reason being... YOU. You are more likely to fail keeping the secret key than someone breaking the AES encryption. For example, if you choose a weak predictable secret key... brute force can quickly take your secret down.

One common mistake might also be to trust other 3rd parties like team mates, co-workers, or applications that might just also neglect your secret until... some attacker gets it.

Finally, if you store a secret key that gives a $200 billion price... someone might actually put $100 billion to the table to buy sufficient computing power to crack your secret. Who knows?

Summary

!! USE A REALLY RANDOM SECRET KEY !!

!! NEVER SHARE IT !!

...and you should be safe ;)