Made breached pwd API comments clearer

This commit is contained in:
Joel Biddle
2023-08-22 19:45:35 +10:00
parent c5ae402787
commit 0b359cd797
2 changed files with 12 additions and 14 deletions
@@ -14,12 +14,11 @@ interface PasswordCheckProps {
const passwordCheck = ({ const passwordCheck = ({
password, password,
setPasswordErrorTooShort, setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorNumber, setPasswordErrorNumber,
setPasswordErrorLowerCase, setPasswordErrorLowerCase,
setPasswordErrorTooLong,
errorCheck errorCheck
}: PasswordCheckProps) => { }: PasswordCheckProps) => {
if (!password || password.length < 14) { if (!password || password.length < 14) {
setPasswordErrorTooShort(true); setPasswordErrorTooShort(true);
errorCheck = true; errorCheck = true;
@@ -27,7 +26,7 @@ const passwordCheck = ({
setPasswordErrorTooShort(false); setPasswordErrorTooShort(false);
} }
if (password.length > 100) { if (password.length > 100) {
setPasswordErrorTooLong(true); setPasswordErrorTooLong(true);
errorCheck = true; errorCheck = true;
} else { } else {
@@ -3,10 +3,10 @@ import crypto from "crypto"; // added types from @types/node
export const checkIsPasswordBreached = async (password: string) => { export const checkIsPasswordBreached = async (password: string) => {
// see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange // see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
// in short, the pending password is hashed (SHA-1), fist 5 chars are sliced and compared against a ranged hash table // in short, the pending password is hashed (SHA-1), the first 5 chars are sliced and compared against a ranged hash table
// if there is a match, that password has been involved in a password breach and should not be accepted // if there is a match, that password has been involved in a password breach and should not be accepted
// the database consists of ~700 mln breached passwords and is continuously being updated including with data from the FBI & the UK's NCA // the database consists of ~700 mln breached passwords and is continuously updated
// https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/ // https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/
const dataBreachCheckAPIBaseURL = "https://api.pwnedpasswords.com/range/"; // added to CSP const dataBreachCheckAPIBaseURL = "https://api.pwnedpasswords.com/range/"; // added to CSP
@@ -15,27 +15,26 @@ export const checkIsPasswordBreached = async (password: string) => {
const textEncoder = new TextEncoder(); const textEncoder = new TextEncoder();
const encodedPwd = textEncoder.encode(password); const encodedPwd = textEncoder.encode(password);
const hash = crypto.createHash("sha1").update(encodedPwd).digest(); const hash = crypto.createHash("sha1").update(encodedPwd).digest();
let hashedPwd = Array.from(new Uint8Array(hash))
const hashedPwd = Array.from(new Uint8Array(hash))
.map((byte) => byte.toString(16).padStart(2, "0")) .map((byte) => byte.toString(16).padStart(2, "0"))
.join("") .join("")
.toUpperCase(); .toUpperCase();
hashedPwd = hashedPwd.slice(0, 5); // ONLY the first five SHA-1 hash chars are sent over HTTPS (the whole string can be sent but that's not very secure due to SHA-1 flaws)
// Ensure that ONLY the first five SHA-1 hash chars are sent over HTTPS const response = await axios.get(`${dataBreachCheckAPIBaseURL}${hashedPwd}`);
const response = await axios.get(`${dataBreachCheckAPIBaseURL}${hashedPwd.slice(0, 5)}`);
const responseData = response.data.toUpperCase(); const responseData = response.data.toUpperCase();
const isBreachedPassword = responseData.includes(hashedPwd.slice(5, 40)); const isBreachedPassword = responseData.includes(hashedPwd.slice(5, 40)); // compare against the API's ranged db's hash table
// Clear the hashed password from memory (good practice) // Clear the hashed password from memory
const zeroBuffer = new Uint8Array(encodedPwd.length); const zeroBuffer = new Uint8Array(encodedPwd.length);
encodedPwd.set(zeroBuffer); encodedPwd.set(zeroBuffer);
return isBreachedPassword; // boolean return isBreachedPassword; // boolean: true === "password has been involved in a data breach"
} catch (err: any) { } catch (err: any) {
if (axios.isAxiosError(err) && err.response && err.response.status === 429) { if (axios.isAxiosError(err) && err.response && err.response.status === 429) {
console.error("Received a 429 response from the Pwnd Passwords API"); console.error("Received a 429 response from the Pwnd Passwords API");
// Handle the 429 error here (not 100% sure what the rate limits are for the password API) // Handle the 429 error here (not 100% sure what the rate limits are for the password API but looks like <10 calls/min)
// an error here should not cause a fail of setting/resetting/changing the password // an error here should not cause the setting/resetting/changing password to fail (unless desired)
} else { } else {
console.error(err); console.error(err);
} }