Skip to content

Instantly share code, notes, and snippets.

@thecoolwinter
Created September 16, 2024 15:10
Show Gist options
  • Save thecoolwinter/e03b33c3d3d0f4124e72a60f3bcf1936 to your computer and use it in GitHub Desktop.
Save thecoolwinter/e03b33c3d3d0f4124e72a60f3bcf1936 to your computer and use it in GitHub Desktop.
/**
* Check a password in the [haveibeenpwned](https://haveibeenpwned.com/Passwords) database.
*
* Uses a secure mechanism for sending passwords according to the API spec. First, the password is hashed using the
* SHA-1 secure hash algorithm. Then, the first 5 characters are sent to the API. The API returns a list of possible
* matches and the calculated hash is used to check if the password exists in the returned list.
*
* @async
* @author Khan Winter (https://github.com/thecoolwinter)
* @license MIT
* @param {String} password The password to check for pwnage.
* @returns {Number} An integer indicating how many times the password has appeared in a security breach. >0 indicates
* it has appeared in one or more, 0 indicates it is a secure password based on this check alone.
*/
export async function pwnd(password) {
const crypto = window.crypto;
let passwordBuffer = new TextEncoder().encode(password);
let shaHash = await crypto.subtle.digest("SHA-1", passwordBuffer);
const hashArray = Array.from(new Uint8Array(shaHash));
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join("")
.toUpperCase();
let prefix = hashHex.slice(0, 5);
let suffix = hashHex.slice(5);
try {
const res = await fetch("https://api.pwnedpasswords.com/range/" + prefix, {
headers: {
"Add-Padding": true,
},
});
const text = await res.text();
let lines = text.split("\n");
for (let line of lines) {
let [hashSuffix, count] = line.split(":");
if (hashSuffix === suffix) {
return parseInt(count);
}
}
return 0;
} catch (error) {
console.error(error);
throw error;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment