Set max password length (100 chars) to help prevent DDOS attack

This commit is contained in:
Joel Biddle
2023-08-22 14:05:00 +10:00
parent 5b342409e3
commit 534d96ffb6
7 changed files with 69 additions and 37 deletions
+5 -4
View File
@@ -231,10 +231,11 @@
"current": "Current password", "current": "Current password",
"current-wrong": "The current password may be wrong", "current-wrong": "The current password may be wrong",
"new": "New password", "new": "New password",
"validate-base": "Password should contain at least:", "validate-base": "Password should contain:",
"validate-length": "14 characters", "validate-too-short": "at least 14 characters",
"validate-case": "1 lowercase character", "validate-too-long": "at most 100 characters",
"validate-number": "1 number" "validate-case": "at least 1 lowercase character",
"validate-number": "at least 1 number"
}, },
"token": { "token": {
"service-tokens": "Service Tokens", "service-tokens": "Service Tokens",
+5 -4
View File
@@ -215,10 +215,11 @@
"current": "Mot de passe actuel", "current": "Mot de passe actuel",
"current-wrong": "Le mot de passe actuel peut être érroné", "current-wrong": "Le mot de passe actuel peut être érroné",
"new": "Nouveau mot de passe", "new": "Nouveau mot de passe",
"validate-base": "Le mot de passe doit contenir au moins:", "validate-base": "Le mot de passe doit contenir:",
"validate-length": "14 caractères", "validate-too-short": "au moins 14 caractères",
"validate-case": "1 caractère miniscule", "validate-too-long": "au maximum 100 caractères",
"validate-number": "1 chiffre" "validate-case": "au moins 1 caractère miniscule",
"validate-number": "au moins 1 chiffre"
}, },
"token": { "token": {
"service-tokens": "Jetons de service", "service-tokens": "Jetons de service",
@@ -39,7 +39,8 @@ interface UserInfoStepProps {
} }
type Errors = { type Errors = {
length?: string, tooShort?: string,
tooLong?: string,
upperCase?: string, upperCase?: string,
lowerCase?: string, lowerCase?: string,
number?: string, number?: string,
@@ -2,7 +2,8 @@
interface PasswordCheckProps { interface PasswordCheckProps {
password: string; password: string;
errorCheck: boolean; errorCheck: boolean;
setPasswordErrorLength: (value: boolean) => void; setPasswordErrorTooShort: (value: boolean) => void;
setPasswordErrorTooLong: (value: boolean) => void;
setPasswordErrorNumber: (value: boolean) => void; setPasswordErrorNumber: (value: boolean) => void;
setPasswordErrorLowerCase: (value: boolean) => void; setPasswordErrorLowerCase: (value: boolean) => void;
} }
@@ -12,17 +13,25 @@ interface PasswordCheckProps {
*/ */
const passwordCheck = ({ const passwordCheck = ({
password, password,
setPasswordErrorLength, setPasswordErrorTooShort,
setPasswordErrorNumber, setPasswordErrorNumber,
setPasswordErrorLowerCase, setPasswordErrorLowerCase,
setPasswordErrorTooLong,
errorCheck errorCheck
}: PasswordCheckProps) => { }: PasswordCheckProps) => {
if (!password || password.length < 14) { if (!password || password.length < 14) {
setPasswordErrorLength(true); setPasswordErrorTooShort(true);
errorCheck = true; errorCheck = true;
} else { } else {
setPasswordErrorLength(false); setPasswordErrorTooShort(false);
}
if (password.length > 100) {
setPasswordErrorTooLong(true);
errorCheck = true;
} else {
setPasswordErrorTooLong(false);
} }
if (!/\d/.test(password)) { if (!/\d/.test(password)) {
@@ -1,5 +1,6 @@
type Errors = { type Errors = {
length?: string, tooShort?: string,
tooLong?: string,
upperCase?: string, upperCase?: string,
lowerCase?: string, lowerCase?: string,
number?: string, number?: string,
@@ -15,11 +16,12 @@ interface CheckPasswordParams {
} }
/** /**
* Validate that the password [password] is at least: * Validate that the password [password]:
* - 8 characters long * - Contains at least 14 characters long
* - Contains 1 uppercase character (A-Z) * - Contains at most 100 characters long
* - Contains 1 lowercase character (a-z) * - Contains at least 1 uppercase character (A-Z)
* - Contains 1 number (0-9) * - Contains at least 1 lowercase character (a-z)
* - Contains at least 1 number (0-9)
* - Does not contain 3 repeat, consecutive characters * - Does not contain 3 repeat, consecutive characters
* *
* The function returns whether or not the password [password] * The function returns whether or not the password [password]
@@ -37,24 +39,28 @@ const checkPassword = ({
}: CheckPasswordParams): boolean => { }: CheckPasswordParams): boolean => {
const errors: Errors = {}; const errors: Errors = {};
if (password.length < 8) { if (password.length < 14) {
errors.length = "8 characters"; errors.tooShort = "at least 14 characters";
}
if (password.length > 100) {
errors.tooLong = "at most 100 characters";
} }
if (!/[A-Z]/.test(password)) { if (!/[A-Z]/.test(password)) {
errors.upperCase = "1 uppercase character (A-Z)"; errors.upperCase = "at least 1 uppercase character (A-Z)";
} }
if (!/[a-z]/.test(password)) { if (!/[a-z]/.test(password)) {
errors.lowerCase = "1 lowercase character (a-z)"; errors.lowerCase = "at least 1 lowercase character (a-z)";
} }
if (!/[0-9]/.test(password)) { if (!/[0-9]/.test(password)) {
errors.number = "1 number (0-9)"; errors.number = "at least 1 number (0-9)";
} }
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
errors.specialChar = "1 special character (!@#$%^&*(),.?)"; errors.specialChar = "at least 1 special character (!@#$%^&*(),.?)";
} }
if (/([A-Za-z0-9])\1\1\1/.test(password)) { if (/([A-Za-z0-9])\1\1\1/.test(password)) {
+24 -11
View File
@@ -28,7 +28,8 @@ export default function PasswordReset() {
const [privateKey, setPrivateKey] = useState(""); const [privateKey, setPrivateKey] = useState("");
const [newPassword, setNewPassword] = useState(""); const [newPassword, setNewPassword] = useState("");
const [backupKeyError, setBackupKeyError] = useState(false); const [backupKeyError, setBackupKeyError] = useState(false);
const [passwordErrorLength, setPasswordErrorLength] = useState(false); const [passwordErrorTooShort, setPasswordErrorTooShort] = useState(false);
const [passwordErrorTooLong, setPasswordErrorTooLong] = useState(false);
const [passwordErrorNumber, setPasswordErrorNumber] = useState(false); const [passwordErrorNumber, setPasswordErrorNumber] = useState(false);
const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false); const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false);
@@ -67,7 +68,8 @@ export default function PasswordReset() {
e.preventDefault(); e.preventDefault();
const errorCheck = passwordCheck({ const errorCheck = passwordCheck({
password: newPassword, password: newPassword,
setPasswordErrorLength, setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorNumber, setPasswordErrorNumber,
setPasswordErrorLowerCase, setPasswordErrorLowerCase,
errorCheck: false errorCheck: false
@@ -221,7 +223,8 @@ export default function PasswordReset() {
setNewPassword(password); setNewPassword(password);
passwordCheck({ passwordCheck({
password, password,
setPasswordErrorLength, setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorNumber, setPasswordErrorNumber,
setPasswordErrorLowerCase, setPasswordErrorLowerCase,
errorCheck: false errorCheck: false
@@ -230,22 +233,32 @@ export default function PasswordReset() {
type="password" type="password"
value={newPassword} value={newPassword}
isRequired isRequired
error={passwordErrorLength && passwordErrorLowerCase && passwordErrorNumber} error={passwordErrorTooShort && passwordErrorTooLong && passwordErrorLowerCase && passwordErrorNumber}
autoComplete="new-password" autoComplete="new-password"
id="new-password" id="new-password"
/> />
</div> </div>
{passwordErrorLength || passwordErrorLowerCase || passwordErrorNumber ? ( {passwordErrorTooShort || passwordErrorTooLong || passwordErrorLowerCase || passwordErrorNumber ? (
<div className="mx-2 mt-3 mb-2 flex w-full max-w-md flex-col items-start rounded-md bg-white/5 px-2 py-2"> <div className="mx-2 mt-3 mb-2 flex w-full max-w-md flex-col items-start rounded-md bg-white/5 px-2 py-2">
<div className="mb-1 text-sm text-gray-400">Password should contain at least:</div> <div className="mb-1 text-sm text-gray-400">Password should contain:</div>
<div className="ml-1 flex flex-row items-center justify-start"> <div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorLength ? ( {passwordErrorTooShort ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" /> <FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : ( ) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" /> <FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)} )}
<div className={`${passwordErrorLength ? "text-gray-400" : "text-gray-600"} text-sm`}> <div className={`${passwordErrorTooShort ? "text-gray-400" : "text-gray-600"} text-sm`}>
14 characters at least 14 characters
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorTooLong ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div className={`${passwordErrorTooLong ? "text-gray-400" : "text-gray-600"} text-sm`}>
at most 100 characters
</div> </div>
</div> </div>
<div className="ml-1 flex flex-row items-center justify-start"> <div className="ml-1 flex flex-row items-center justify-start">
@@ -257,7 +270,7 @@ export default function PasswordReset() {
<div <div
className={`${passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"} text-sm`} className={`${passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"} text-sm`}
> >
1 lowercase character at least 1 lowercase character
</div> </div>
</div> </div>
<div className="ml-1 flex flex-row items-center justify-start"> <div className="ml-1 flex flex-row items-center justify-start">
@@ -267,7 +280,7 @@ export default function PasswordReset() {
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" /> <FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)} )}
<div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}> <div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}>
1 number at least 1 number
</div> </div>
</div> </div>
</div> </div>
@@ -18,7 +18,8 @@ import { useUser } from "@app/context";
import { useGetCommonPasswords } from "@app/hooks/api"; import { useGetCommonPasswords } from "@app/hooks/api";
type Errors = { type Errors = {
length?: string, tooShort?: string,
tooLong?: string,
upperCase?: string, upperCase?: string,
lowerCase?: string, lowerCase?: string,
number?: string, number?: string,