Password breach check

This commit is contained in:
Joel Biddle
2023-08-22 16:49:17 +10:00
parent fbeb210965
commit 0eb21919fb
6 changed files with 73 additions and 9 deletions
@@ -46,6 +46,7 @@ type Errors = {
number?: string, number?: string,
specialChar?: string, specialChar?: string,
repeatedChar?: string, repeatedChar?: string,
breachedPassword?: string
}; };
/** /**
@@ -101,7 +102,7 @@ export default function UserInfoStep({
setOrganizationNameError(false); setOrganizationNameError(false);
} }
errorCheck = checkPassword({ errorCheck = await checkPassword({
password, password,
commonPasswords, commonPasswords,
setErrors setErrors
@@ -0,0 +1,51 @@
import axios from "axios";
import crypto from "crypto";
///// REMINDER: ensure all logs are deleted!!! /////
export const checkIsPasswordBreached = async (password: string) => {
const dataBreachCheckAPIBaseURL = "https://api.pwnedpasswords.com/range/";
try {
const textEncoder = new TextEncoder();
const encodedPwd = textEncoder.encode(password);
console.log("encodedPwd:", encodedPwd); // delete later!!!
const hashBuffer = await crypto.subtle.digest("SHA-1", encodedPwd);
console.log("hashBuffer:", hashBuffer); // delete later!!!
const hashedPwd = Array.from(new Uint8Array(hashBuffer))
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("")
.toUpperCase();
console.log("hashedPwd:", hashedPwd); // delete later!!!
const response = await axios.get(
`${dataBreachCheckAPIBaseURL}${hashedPwd.slice(0, 5)}`
);
console.log("response:", response); // delete later!!!
const responseData = response.data.toUpperCase();
console.log("responseData:", responseData); // delete later!!!
const isBreachedPassword = responseData.includes(hashedPwd.slice(5, 40));
console.log("isBreachedPassword:", isBreachedPassword); // delete later!!!
// Clear the hashed password from memory
crypto.subtle.digest("SHA-1", encodedPwd);
return isBreachedPassword;
} catch (err: any) {
if (
axios.isAxiosError(err) &&
err.response &&
err.response.status === 429
) {
console.error("Received a 429 response from the Pwnd Passwords API");
// Handle the 429 error here
} else {
console.error(err);
}
}
};
@@ -1,3 +1,5 @@
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
type Errors = { type Errors = {
tooShort?: string, tooShort?: string,
tooLong?: string, tooLong?: string,
@@ -6,7 +8,8 @@ type Errors = {
number?: string, number?: string,
specialChar?: string, specialChar?: string,
repeatedChar?: string, repeatedChar?: string,
commonPassword?: string commonPassword?: string,
breachedPassword?: string
}; };
interface CheckPasswordParams { interface CheckPasswordParams {
@@ -32,13 +35,15 @@ interface CheckPasswordParams {
* @param {String} obj.password - the password to check * @param {String} obj.password - the password to check
* @param {Function} obj.setErrors - set state function to set error object * @param {Function} obj.setErrors - set state function to set error object
*/ */
const checkPassword = ({ const checkPassword = async ({
password, password,
commonPasswords, commonPasswords,
setErrors setErrors
}: CheckPasswordParams): boolean => { }: CheckPasswordParams): Promise<boolean> => {
const errors: Errors = {}; const errors: Errors = {};
const isBreachedPassword = await checkIsPasswordBreached(password)
if (password.length < 14) { if (password.length < 14) {
errors.tooShort = "at least 14 characters"; errors.tooShort = "at least 14 characters";
} }
@@ -70,6 +75,10 @@ const checkPassword = ({
if (commonPasswords.includes(password)) { if (commonPasswords.includes(password)) {
errors.commonPassword = "No common passwords"; errors.commonPassword = "No common passwords";
} }
if (isBreachedPassword) {
errors.breachedPassword = "The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password.";
}
setErrors(errors); setErrors(errors);
return Object.keys(errors).length > 0; return Object.keys(errors).length > 0;
+2 -1
View File
@@ -41,6 +41,7 @@ type Errors = {
number?: string, number?: string,
specialChar?: string, specialChar?: string,
repeatedChar?: string, repeatedChar?: string,
breachedPassword?: string
}; };
export default function SignupInvite() { export default function SignupInvite() {
@@ -80,7 +81,7 @@ export default function SignupInvite() {
setLastNameError(false); setLastNameError(false);
} }
errorCheck = checkPassword({ errorCheck = await checkPassword({
password, password,
commonPasswords, commonPasswords,
setErrors setErrors
@@ -25,6 +25,7 @@ type Errors = {
number?: string, number?: string,
specialChar?: string, specialChar?: string,
repeatedChar?: string, repeatedChar?: string,
breachedPassword?: string
}; };
const schema = yup.object({ const schema = yup.object({
@@ -53,8 +54,8 @@ export const ChangePasswordSection = () => {
try { try {
if (!user?.email) return; if (!user?.email) return;
if (!commonPasswords) return; if (!commonPasswords) return;
const errorCheck = checkPassword({ const errorCheck = await checkPassword({
password: newPassword, password: newPassword,
commonPasswords, commonPasswords,
setErrors setErrors
@@ -42,6 +42,7 @@ type Errors = {
number?: string, number?: string,
specialChar?: string, specialChar?: string,
repeatedChar?: string, repeatedChar?: string,
breachedPassword?: string
}; };
/** /**
@@ -99,7 +100,7 @@ export const UserInfoSSOStep = ({
setOrganizationNameError(false); setOrganizationNameError(false);
} }
errorCheck = checkPassword({ errorCheck = await checkPassword({
password, password,
commonPasswords, commonPasswords,
setErrors setErrors