/encrypt-workers-kv

Encrypted put and get functions for Cloudflare Workers KV

Primary LanguageTypeScriptMIT LicenseMIT

Encrypt Cloudflare Workers KV

This library provides wrappers on the put and get functions from the Cloudflare Workers runtime API for writing to and reading from Workers KV, encrypting values before put and decrypting values after get. Encryption is implemented using the Web Crypto API to derive AES-GCM keys from a password based key (PBKDF2).

By default all data stored in Cloudflare Workers KV is encrypted at rest:

All values are encrypted at rest with 256-bit AES-GCM, and only decrypted by the process executing your Worker scripts or responding to your API requests. (docs)

However, there are a variety of reasons you may want to add your own encryption to the values stored in Workers KV. For example, permissions to access Workers KV are scoped at the account level. For those working in shared team or organizational accounts, this means you cannot limit access to specific KV namespaces, as anyone in that account with access to Workers can read the stored data in all KV namespaces in that account.

Installation

Use npm to install this package while in the root directory of your Workers project:

npm i encrypt-workers-kv

Usage

putEncryptedKV(namespace, key, data, password, options)

Wrapper on Workers KV put command that encrypts data prior to storage

getDecryptedKV(namespace, key, password)

Wrapper on Workers KV get command that decrypts data after getting from storage

putEncryptedKV(namespace, key, data, password, options)

Wrapper on Workers KV put command that encrypts data prior to storage

Param Type Description
namespace KVNamespace the binding to the namespace that script references
key string the key in the namespace used to reference the stored value
data string or ArrayBuffer the data to encrypt and store in KV
password string the password to be used to encrypt the data
iterations number optional number of iterations used by the PBKDF2 to derive the key. Default 10000
options Object optional KV put fields (docs)

Returns encrypted value as string - Promise<ArrayBuffer>

Sample implementation:

let data = await request.text()
try {
  await putEncryptedKV(ENCRYPTED, 'data', data, password)
  return new Response('Secret stored successfully')
} catch (e) {
  return new Response(e.message, { status: e.status })
}

getDecryptedKV(namespace, key, password)

Wrapper on Workers KV get command that decrypts data after getting from storage

Param Type Description
namespace KVNamespace the binding to the namespace that script references
key string the key in the namespace used to reference the stored value
password string the password used to encrypt the data

Returns decrypted value as string - Promise<ArrayBuffer>

Sample implementation:

try {
  let decryptedData = await getDecryptedKV(ENCRYPTED, 'data', password)
  let strDecryptedData = dec.decode(decryptedData)
  return new Response(`${strDecryptedData}`, {
    headers: { 'content-type': 'text/html; charset=utf-8' },
  })
} catch (e) {
  return new Response(e.message, { status: e.status })
}

Logic Flow

An explanation of the steps used behind the scenes for encryption and decryption in src/index.ts.

Encryption:

  1. Creates a password based key (PBKDF2) that will be used to derive the AES-GCM key used for encryption / decryption.
  2. Creates an AES-GCM key using the PBKDF2 key and a randomized salt value.
  3. Encrypts the input data using the AES-GCM key and a randomized initialization vector (iv).
  4. The values used for the password, salt, iv for encryption are needed for decryption. Therefore, creates an ArrayBuffer to be stored that includes the salt that was used when creating the password based key (PBKDF2), iv used for creating the AES key, and the encrypted content. The password should remain secret, so recommend storing it as a Worker Secret.

Decryption:

  1. Derives the salt, iv, and encrypted data from the ArrayBuffer.
  2. Creates a password based key (PBKDF2) that will be used to derive the AES-GCM key used for encryption / decryption. Password must be the same used for encryption and is obtained from the Workers Secret.
  3. Creates an AES-GCM key using the PBKDF2 key and the salt from the ArrayBuffer.
  4. Decrypts the input data using the AES-GCM key and the iv from the ArrayBuffer.

Build and Test

A test worker is used to test the library, located in test-worker/. Configure wrangler.toml in that folder with your account information. Then, create a new Workers KV namespace and add the configuration to wrangler.toml.

wrangler kv:namespace create "ENCRYPTED"

Add the password for PBKDF2 as a Workers Secret.

wrangler secret put PASSWORD

To deploy the test worker and run the the automated tests, change directory back to the project root directory and:

npm run test:deploy && npm run test

Sample Implementation

A sample implementation is available in the Workers Azure AD auth example, which uses encrypt-workers-kv to encrypt session token information stored as values in Workers KV.

References

Further info on Web Crypto usage: