Encrypting Data With Node.js
In a recent side project I came upon the need to securely store credentials
in a database that I can later retrieve and use as part of the application.
I know that Node.js has a very extensive crypto
module, so I knew it was
possible, though I wasn’t sure on the specifics.
Typically when storing passwords or other similar secrets, values are
hashed but in my case I needed to encrypt/decrypt data since I need to
access the original value after accessing it from my database. To make this
work, we can use the createCipheriv
function along with a secret key and
initialization vector (IV) to encrypt and decrypt our data.
import crypto from "node:crypto"
const algorithm = "aes-256-cbc"
const secretKey = process.env.SECRET_KEY
if (!secretKey) {
throw new Error("SECRET_KEY environment variable is required")
}
const key = crypto
.createHash("sha512")
.update(secretKey)
.digest("hex")
.substring(0, 32)
const iv = crypto.randomBytes(16)
export function encrypt(data: string) {
const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv)
let encrypted = cipher.update(data, "utf-8", "hex")
encrypted += cipher.final("hex")
// Package the IV and encrypted data together so it can be stored in a single
// column in the database.
return iv.toString("hex") + encrypted
}
export function decrypt(data: string) {
// Unpackage the combined iv + encrypted message. Since we are using a fixed
// size IV, we can hard code the slice length.
const inputIV = data.slice(0, 32)
const encrypted = data.slice(32)
const decipher = crypto.createDecipheriv(
algorithm,
Buffer.from(key),
Buffer.from(inputIV, "hex"),
)
let decrypted = decipher.update(encrypted, "hex", "utf-8")
decrypted += decipher.final("utf-8")
return decrypted
}
The nice thing about this implementation is that it returns a plain
string
with the IV and encrypted data sandwiched together. As long as you
use the decrypt
function, it knows how to properly decrypt the value
making it really simple to store in a database.
const password = "my secret password, don't tell anyone!"
const encrypted = encrypt(password)
const decrypted = decrypt(encrypted)
I’ll probably also explore re-implementing this same logic using the web crypto API. But that’s for another day!