Password Security
Password security is one of the most important parts of protecting accounts and systems. Whether you are building a website, using social media, or managing a company application, passwords are often the first line of defense.
This explanation will help you understand:
What Is Password Security
Password security refers to the methods used to:
A password is meant to prove identity. If someone else knows your password, they can pretend to be you.
Why Passwords Get Hacked
Passwords are often compromised because of:
Understanding these threats helps you understand why security measures are necessary.
Strong Password Characteristics
A strong password should:
Example of weak password:
password123
Example of strong password:
T9#qL!7vP2@x
However, long passphrases are often even better:
correct-horse-battery-staple-92
Length is more important than complexity alone.
Password Hashing Explained
One of the most important rules in password security:
Never store passwords in plain text.
Plain text means storing passwords exactly as users type them.
Example of bad practice:
users.push({ username: "admin", password: "admin123" });
If the database is leaked, attackers immediately know all passwords.
Instead, passwords must be hashed.
What Is Hashing
Hashing is a one-way mathematical process that converts data into a fixed-length string.
Important properties:
Example using Node.js crypto module:
const crypto = require('crypto');
const password = "admin123";
const hash = crypto.createHash('sha256')
.update(password)
.digest('hex');
console.log(hash);
But basic hashing like SHA-256 is not enough for password storage.
Why Simple Hashing Is Not Enough
Attackers use:
Fast hashing algorithms (like SHA-256) are designed for speed, which makes brute force attacks easier.
For passwords, we need slow hashing algorithms.
Secure Password Hashing Algorithms
Use specialized password hashing algorithms such as:
These algorithms are intentionally slow and include salting.
What Is Salting
A salt is a random value added to a password before hashing.
Why it matters:
If two users have the same password:
Without salt:
They will have identical hashes.
With salt:
Each user gets a different hash.
Salting prevents attackers from using precomputed tables.
Secure Password Storage Example Using bcrypt
First, install bcrypt:
npm install bcrypt
Now example code:
const bcrypt = require('bcrypt');
async function registerUser(username, password) {
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);
console.log("Stored in database:");
console.log({ username, password: hashedPassword });
}
async function loginUser(inputPassword, storedHash) {
const match = await bcrypt.compare(inputPassword, storedHash);
if (match) {
console.log("Login successful");
} else {
console.log("Invalid password");
}
}
This does:
Password Verification Flow
When a user logs in:
Important: The original password is never stored.
Protecting Passwords In Transit
Passwords must also be protected during transmission.
Always use HTTPS.
HTTPS encrypts data between browser and server.
Without HTTPS:
Anyone on the same network could capture passwords.
Preventing Brute Force Attacks
Even strong passwords can be attacked if unlimited attempts are allowed.
Protection methods:
Multi-Factor Authentication
Multi-Factor Authentication adds another layer beyond passwords.
Examples:
Even if password is stolen, attacker still needs second factor.
Common Developer Mistakes
Avoiding these mistakes dramatically improves security.
Simple Vulnerable Example
Bad practice:
let users = [];
function register(username, password) {
users.push({ username, password });
}
function login(username, password) {
const user = users.find(u =>
u.username === username && u.password === password
);
return user ? "Login success" : "Login failed";
}
This stores passwords in plain text and is completely insecure.
Secure Version Example
const bcrypt = require('bcrypt');
let users = [];
async function register(username, password) {
const hashedPassword = await bcrypt.hash(password, 12);
users.push({ username, password: hashedPassword });
}
async function login(username, password) {
const user = users.find(u => u.username === username);
if (!user) return "Login failed";
const match = await bcrypt.compare(password, user.password);
return match ? "Login success" : "Login failed";
}
This is significantly more secure.
const bcrypt = require('bcrypt');
let users = [];
async function register(username, password) {
const hashedPassword = await bcrypt.hash(password, 12);
users.push({ username, password: hashedPassword });
console.log("User registered securely");
}
async function login(username, password) {
const user = users.find(u => u.username === username);
if (!user) {
console.log("Login failed");
return;
}
const match = await bcrypt.compare(password, user.password);
if (match) {
console.log("Login success");
} else {
console.log("Login failed");
}
}
(async () => {
await register("admin", "StrongPassword123!");
await login("admin", "StrongPassword123!");
})();