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

View File

@@ -46,6 +46,7 @@ type Errors = {
number?: string,
specialChar?: string,
repeatedChar?: string,
breachedPassword?: string
};
/**
@@ -101,7 +102,7 @@ export default function UserInfoStep({
setOrganizationNameError(false);
}
errorCheck = checkPassword({
errorCheck = await checkPassword({
password,
commonPasswords,
setErrors

View File

@@ -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);
}
}
};

View File

@@ -1,3 +1,5 @@
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
type Errors = {
tooShort?: string,
tooLong?: string,
@@ -6,7 +8,8 @@ type Errors = {
number?: string,
specialChar?: string,
repeatedChar?: string,
commonPassword?: string
commonPassword?: string,
breachedPassword?: string
};
interface CheckPasswordParams {
@@ -32,13 +35,15 @@ interface CheckPasswordParams {
* @param {String} obj.password - the password to check
* @param {Function} obj.setErrors - set state function to set error object
*/
const checkPassword = ({
const checkPassword = async ({
password,
commonPasswords,
setErrors
}: CheckPasswordParams): boolean => {
}: CheckPasswordParams): Promise<boolean> => {
const errors: Errors = {};
const isBreachedPassword = await checkIsPasswordBreached(password)
if (password.length < 14) {
errors.tooShort = "at least 14 characters";
}
@@ -70,6 +75,10 @@ const checkPassword = ({
if (commonPasswords.includes(password)) {
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);
return Object.keys(errors).length > 0;

View File

@@ -41,6 +41,7 @@ type Errors = {
number?: string,
specialChar?: string,
repeatedChar?: string,
breachedPassword?: string
};
export default function SignupInvite() {
@@ -80,7 +81,7 @@ export default function SignupInvite() {
setLastNameError(false);
}
errorCheck = checkPassword({
errorCheck = await checkPassword({
password,
commonPasswords,
setErrors

View File

@@ -25,6 +25,7 @@ type Errors = {
number?: string,
specialChar?: string,
repeatedChar?: string,
breachedPassword?: string
};
const schema = yup.object({
@@ -53,8 +54,8 @@ export const ChangePasswordSection = () => {
try {
if (!user?.email) return;
if (!commonPasswords) return;
const errorCheck = checkPassword({
const errorCheck = await checkPassword({
password: newPassword,
commonPasswords,
setErrors

View File

@@ -42,6 +42,7 @@ type Errors = {
number?: string,
specialChar?: string,
repeatedChar?: string,
breachedPassword?: string
};
/**
@@ -99,7 +100,7 @@ export const UserInfoSSOStep = ({
setOrganizationNameError(false);
}
errorCheck = checkPassword({
errorCheck = await checkPassword({
password,
commonPasswords,
setErrors