Skip to content

Instantly share code, notes, and snippets.

@jgrahamc
Created February 24, 2018 16:36
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save jgrahamc/21f31c8fb4b2c27bda4f605197d5143f to your computer and use it in GitHub Desktop.
Save jgrahamc/21f31c8fb4b2c27bda4f605197d5143f to your computer and use it in GitHub Desktop.
Cloudflare Workers that adds an "Cf-Password-Pwnd" header to a POST request indicating whether the 'password' field appears in Troy Hunt's database of pwned passwords.
addEventListener('fetch', event => {
event.respondWith(fetchAndCheckPassword(event.request))
})
async function fetchAndCheckPassword(req) {
if (req.method == "POST") {
try {
const post = await req.formData();
const pwd = post.get('password')
const enc = new TextEncoder("utf-8").encode(pwd)
let hash = await crypto.subtle.digest("SHA-1", enc)
let hashStr = hex(hash).toUpperCase()
const prefix = hashStr.substring(0, 5)
const suffix = hashStr.substring(5)
const pwndpwds = await fetch('https://api.pwnedpasswords.com/range/' + prefix)
const t = await pwndpwds.text()
const pwnd = t.includes(suffix)
let newHdrs = new Headers(req.headers)
newHdrs.set('Cf-Password-Pwnd', pwnd?'YES':'NO')
const init = {
method: 'POST',
headers: newHdrs,
body: post
}
return await fetch(req.url, init)
} catch (err) {
return new Response('Internal Error')
}
}
return await fetch(req)
}
function hex(a) {
var h = "";
var b = new Uint8Array(a);
for(var i = 0; i < b.length; i++){
var hi = b[i].toString(16);
h += hi.length === 1?"0"+hi:hi;
}
return h;
}
@sejoker
Copy link

sejoker commented Feb 26, 2018

Outside of try/catch blocks, return await is redundant.

@kentonv
Copy link

kentonv commented Feb 26, 2018

So, I had previously commented here that you should change:

return await fetch(req.url, init)

to:

return await fetch(req, init)

By passing a whole Request object as the first parameter, rather than just a URL, you make sure to copy all of the properties of the original request -- except for the ones that are overridden by init, of ocurse. This is important, for example, because you want to make sure to use the same redirect mode (the req.redirect property). This script as written currently will unfortunately cause POST redirects to be handled at the worker level rather than returned to the client (because the default redirect mode is "follow", but for incoming proxied requests it's "manual").

However, this correction doesn't work! This code will throw an exception complaining that req.body is already used. But init is providing a new body, so this shouldn't matter.

This appears to be a bug in the Fetch API spec (not just our implementation). We've filed: whatwg/fetch#674

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment