mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 06:26:27 +00:00
Password breach check
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+3
-2
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user