> mthadley_

Crypto in the Browser

No, don’t worry, I’m not writing about that kind of crypto. Instead, I’m referring to the SubtleCrypto API found in modern browsers. Oh, and technically not in a browser, since I’m writing about Cloudflare Workers, but eh, close enough.

I recently found myself needing to verify the authenticity of incoming webhook payloads in a Cloudflare Worker. Realizing the worker runtime is “browser-like”, and therefore implements the SubtleCrypto interface, I finally had a reason to try it out.

Importing the Key

The service sending the webhooks signed their payload with a shared secret, which was generated roughly like this:

$ ruby -r securerandom -e "puts SecureRandom.alphanumeric(64)"
A4S7Fd0ZPbOTVkYtjxyIbrHT60lDszkdw3mCkNEPuahwYQGDKCm5Cd72QmMxGrWF

Before verifying anything, the secret needs to be imported as a key. In my case, the signing algorithm was HMAC-SHA256, for which a key can be imported like this:

const keyData = new TextEncoder().encode(sharedSecret)

const key = crypto.subtle.importKey(
  "raw",
  keyData,
  {
    name: "HMAC",
    hash: "SHA-256",
  },
  true,
  ["verify"]
)

importKey() returns a CryptoKey, which can be used for whatever operations were specified as the last argument, but for my purposes, only needs to be verify.

Verification

With key in hand, we can use it for verification purposes via the appropriately named verify() method. It expects the to-be-verified data as an ArrayBuffer, so the arrayBuffer() method on Request is very helpful here:

const signature = request.headers.get("X-Signature")
const body = await request.arrayBuffer()

const isValid = await crypto.subtle.verify("HMAC", key, signature, body)

From there, you can return 401 Unauthorized if the signatures don’t match, or whatever other action you’d like to take.

Other Possibilities

I think it’s pretty cool you can do such advanced cryptographic operations in the browser with fairly straightforward API’s. It makes me wonder what other use-cases they unlock.

One example I came across was the Portable Secret project. It uses these same API’s to output a self-contained HTML file that can self-decrypt it’s contents when given the correct password. That’s pretty neat.