From 3b1c3853cc622bc30fd9575801552b60aea1e9f7 Mon Sep 17 00:00:00 2001 From: Ardis Lu Date: Thu, 3 Jun 2021 22:45:06 -0700 Subject: [PATCH] Add pwned worker --- README.md | 19 +++++++++++++++++++ package.json | 3 ++- pwned/index.js | 25 +++++++++++++++++++++++++ pwned/package.json | 22 ++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 pwned/index.js create mode 100644 pwned/package.json diff --git a/README.md b/README.md index e0f0635..c3acfb7 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,22 @@ This reverse proxy injects permissive CORS response headers to a response. Usefu ### Usage Same as `proxy`. + +## pwned + +Checks a password against the [have i been pwned?](https://haveibeenpwned.com/) API and returns a boolean indicating if the password has been pwned or not. This is a simplified re-implementation of the [Pwned Passwords Cloudflare Worker](https://github.com/HaveIBeenPwned/PwnedPasswordsCloudflareWorker). + +### Usage + +Pass a plaintext password as the entire query string to the endpoint. For example, if your Cloudflare Worker domain is +``` +https://x.y.workers.dev +``` +and the password you want to check is +``` +hunter1 +``` +then the final request URL should be +``` +https://x.y.workers.dev?hunter1 +``` \ No newline at end of file diff --git a/package.json b/package.json index 1ed7537..1c13088 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "workspaces": [ "proxy", "instagram", - "cors" + "cors", + "pwned" ] } diff --git a/pwned/index.js b/pwned/index.js new file mode 100644 index 0000000..a2db1ae --- /dev/null +++ b/pwned/index.js @@ -0,0 +1,25 @@ +async function handleRequest(request) { + const url = new URL(request.url); + const queryString = decodeURIComponent(url.search.substring(1)); + + // Convert password string to byte array and hash it, then convert hashed byte array to hex string + const inputArray = new TextEncoder().encode(queryString); + const digest = await crypto.subtle.digest('SHA-1', inputArray); + const outputArray = [...new Uint8Array(digest)]; // Need to spread typed array into a non-typed array to allow .map() to output strings + const hash = outputArray.map(v => v.toString(16).padStart(2, '0')).join('').toUpperCase(); + + // Split the hash as required by the HIBP API (k-Anonymity model) + const range = hash.substring(0, 5); + const index = hash.substring(5); + + const result = fetch(`https://api.pwnedpasswords.com/range/${range}`) + .then(response => response.text()) + .then(body => body.includes(index)) + .then(bool => new Response(bool)); + + return result; +} + +addEventListener('fetch', event => { + event.respondWith(handleRequest(event.request)); +}) diff --git a/pwned/package.json b/pwned/package.json new file mode 100644 index 0000000..e0d69b1 --- /dev/null +++ b/pwned/package.json @@ -0,0 +1,22 @@ +{ + "name": "pwned", + "version": "0.1.0", + "description": "This is a re-implementation of the Pwned Passwords Cloudflare Worker.", + "private": true, + "main": "index.js", + "scripts": { + "login": "wrangler login", + "preview": "wrangler preview", + "build": "wrangler build", + "publish": "wrangler publish" + }, + "author": "Ardis Lu", + "license": "ISC", + "devDependencies": { + "@cloudflare/wrangler": "^1.16.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/ardislu/cloudflare-workers.git" + } +} -- 2.47.3