Crafting Strong Passwords:
Best Practices for Secure Signups

Published on: August 20, 2024

In this comprehensive guide, we’ll explore the foundational elements of secure authentication.

We’ll start with the best practices for creating and managing passwords,
delve into the importance of generating secure hashes,
and walk through the process of comparing hashes during login.

What makes a password secure?

A good strong password is essential for securing user accounts and protecting sensitive information.

According to the OWASP (Open Web Application Security Project) guidelines,
a strong password should adhere to several key principles to minimize the risk of unauthorized access.

Implementing password requirements

To ensure that our application adheres to strong password practices, we’ll be using the

OWASP Password Strength Test library by NowSecure.
This library is designed to help enforce password requirements on the client side and validate passwords on the authentication service.

Example implementation in React
1import owasp from 'owasp-password-strength-test';
2
3owasp.config({
4	allowPassphrases: true,
5	maxLength: 128,
6	minLength: 10,
7	minPhraseLength: 20,
8	minOptionalTestsToPass: 4,
9});
10
11const passwdStrength = owasp.test(value);
12interface PasswordInputProps {
13	title?: string;
14	characterLimit?: number;
15}
16
17const PasswordInput = ({
18	title = '',
19	characterLimit = 128,
20}: PasswordInputProps) => {
21	const [value, setValue] = useState('');
22	const passwdStrength = owasp.test(value);
23
24	const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
25		const { value } = e.target;
26		if (value.length > characterLimit) return;
27		setValue(value.replace(/[^+a-z0-9áéíóúñü_\-.!@#$%^&*(){}[\]]/gi, ''));
28	};
29
30	const isError = value.length > 0 && passwdStrength.errors.length > 0 || false;
31
32	return (
33		<div className={`${styles['password-input']}
34				${isError ? styles['password-input--error'] : ''}`}>
35			<div className={styles['password-input__label']}>
36				<p>{title}</p>
37				<span>*</span>
38				<cite>{`${value.length} / ${characterLimit}`}</cite>
39			</div>
40			<input
41				tabIndex={0}
42				placeholder="Enter your password"
43				onChange={handleChange}
44				type="password"
45				value={value}
46			/>
47			<div className={styles['password-input__strength']}>
48				<cite className="error">
49					{value.length > 0 ? passwdStrength?.errors?.[0] : ''}
50				</cite>
51				<cite>{passwdStrength.strong ? 'Strong' : 'Weak'}</cite>
52			</div>
53		</div>
54	);
55};
56
57export default PasswordInput;
58

Password

*0 / 128
Weak
Verify password strength on server-side

To ensure that the password meets the minimum requirements, we should also validate it on the server-side before hashing it.

We can use the same OWASP library to validate the password on the server-side as well.

Hashing passwords for secure storage

When storing passwords in a database, it’s crucial to hash them securely to prevent unauthorized access.

We’ll be using the bcrypt.js library to hash and compare passwords in our application.

Example implementation in Node.js
1import bcrypt from 'bcrypt';
2
3export const generateHash = (password: string) =>
4	new Promise<string>((resolve, reject) => {
5		bcrypt.hash(password, 10, (error, hash) => {
6			if (error) {
7				return reject(errors.HASHING_FAILED);
8			}
9			return resolve(hash);
10		});
11	});
The resulting hash looks something like this:
1$2b$10$<22-character-salt><31-character-hash>
  • $2b$ - The bcrypt algorithm version
  • 10$ - The cost factor
  • 22-character-salt - The random salt used for hashing
  • 31-character-hash - The hashed password

Bcrypt securely hashes passwords by generating a unique, random salt for each password, ensuring that even identical passwords result in different hashes, effectively protecting against rainbow table and brute-force attacks.


You can learn more about bcrypt here.

Comparing hashed passwords during login

When a user logs in, we need to compare the hashed password stored in the database with the hashed password entered by the user.

Example implementation in Node.js
1import bcrypt from 'bcrypt';
2
3export const verifyHash = (password: string, hash: string) =>
4	new Promise<boolean>((resolve, reject) => {
5		bcrypt.compare(password, hash, (error, result) => {
6			if (error) {
7				return reject(errors.HASH_VERIFICATION_FAILED);
8			}
9			return resolve(result);
10		});
11	});
Conclusion
  • Implementing secure password management practices is crucial for protecting user accounts and sensitive information.
  • By enforcing strong password requirements and securely hashing passwords, we can minimize the risk of unauthorized access and protect user data.
  • Remember to validate passwords on both the client and server side to ensure that they meet the minimum requirements before hashing them.
Whats Next?

Now that you’ve mastered the basics of secure password management, you’re ready to take your authentication system to the next level.

Check out the following blog posts to learn more about securing user sessions and implementing JWT authentication in your application:

Mastering JWT in Node.js: Secure Token Generation and Verification

In this article