Skip to content

Intermediate · Authentication and authorization · topic 31 of 54 · 30 min

Storing passwords safely

Explanation

Never store a password as it was typed. Store a hash: a one-way fingerprint made by a slow password-hashing function such as Argon2id or bcrypt, with a random salt for every user. At login you hash the typed password again and compare the results.

Two words first

Authentication means checking who you are (logging in). Authorization means checking what you are allowed to do (can you delete this order?). This module covers both; this topic is about the first step of authentication: keeping passwords safe.

Why plain-text passwords are a disaster

Databases leak: a stolen backup, a hacked server, a careless employee. If passwords are stored as typed, every account is open at once. Worse, many people use the same password for email and banking, so one leak hurts them everywhere.

Hashing is not encryption

HashingEncryption
DirectionOne way: you cannot get the input backTwo way: decrypt with the key
Needs a key?No (a salt, which is not secret)Yes, and the key must stay secret
Use it forPasswordsData you must read again, like a stored PAN for KYC
Think of it like this: Hashing is like making mango juice: you can always turn the same mango into the same juice, but nobody can turn juice back into a mango. At login you squeeze the typed password again and check that the juice tastes the same.

Salt: different hash for the same password

A salt is a random value made for each user and stored next to their hash. Two users with the password "india@123" get completely different hashes. This stops attackers from using rainbow tables, which are huge pre-computed lists of hashes for common passwords.

Slow on purpose

General hashes like SHA-256 are built to be fast: a gaming GPU can try billions per second, so short passwords fall in minutes. Password hashes are deliberately slow and use lots of memory, so each guess costs the attacker real time and money.

AlgorithmAdvice in 2026
Argon2idFirst choice. OWASP minimum: 19 MiB memory, 2 iterations, 1 thread
scryptGood when Argon2id is not available
bcryptFine for older systems; cost 10 or more; only the first 72 bytes of a password count
PBKDF2-SHA256When rules require FIPS; OWASP suggests 600,000 iterations
SHA-256 / MD5 aloneNever for passwords: far too fast
javascript
// Example only: runs on a server with Node.js and the argon2 package
import argon2 from 'argon2';

// Sign up: hash and store the result (salt is included inside it)
const hash = await argon2.hash(password, { type: argon2.argon2id });
// hash looks like: $argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash>

// Log in: compare the typed password with the stored hash
const ok = await argon2.verify(user.passwordHash, typedPassword);

// bcrypt works the same way:
// const hash = await bcrypt.hash(password, 12);
// const ok = await bcrypt.compare(typedPassword, hash);
Real apps use a library. The stored string holds the algorithm, settings, salt and hash together.
javascript
// PBKDF2 with Web Crypto (built into Node and browsers)
const enc = new TextEncoder();
const key = await crypto.subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, ['deriveBits']);
const bits = await crypto.subtle.deriveBits(
  { name: 'PBKDF2', hash: 'SHA-256', salt: enc.encode(salt), iterations: 600000 },
  key,
  256 // 256 bits = 32 bytes
);
importKey turns the password into key material; deriveBits runs PBKDF2 and gives 32 bytes.
Careful: Compare hashes in constant time. A normal === stops at the first different character, and attackers can measure that tiny time difference. In Node use crypto.timingSafeEqual, or a loop that always checks every character.
Tip: Also: rate-limit login attempts, reject passwords found in known breach lists, and re-hash with stronger settings when a user logs in with an old hash.

Watch the lesson

Storing passwords safely · 1/6

Storing passwords safely

Today: how to store passwords so that even a stolen database does not reveal them.

Prefer a video by a teacher? Find free videos on “password hashing salt bcrypt argon2 explained” on YouTube ↗

In real life: A leak that did not hurt

Imagine a coaching institute's student portal is hacked and the users table is stolen. If the table holds plain passwords, attackers log in to students' email and UPI apps with the same passwords that very day. If it holds Argon2id hashes with salts, each guess costs real computing time and every user needs a separate attack. The institute still resets all passwords, but most students are safe.

Practice (0/3 done)

Task 1 · Code · Javascript

Hashes come out as bytes. Write toHex(bytes) that turns an array (or Uint8Array) of numbers 0-255 into a lower-case hex string, two characters per byte. toHex([0, 15, 255]) is "000fff".

Task 2 · Code · Javascript

Write async hashPassword(password, salt, iterations) using Web Crypto PBKDF2 with SHA-256. Import the password with crypto.subtle.importKey("raw", ..., "PBKDF2", false, ["deriveBits"]), call crypto.subtle.deriveBits with { name: "PBKDF2", hash: "SHA-256", salt, iterations } and 256 bits, and return the result as a 64-character hex string. (Tests use small iteration counts so they run fast; real apps use 600,000 or Argon2id.)

Task 3 · Code · Javascript

hashPassword is done for you. Write safeEqual(a, b) that compares two strings without stopping at the first difference (return false at once only if the lengths differ), and async verifyPassword(password, salt, iterations, storedHex) that hashes the typed password and returns true only when it matches storedHex, using safeEqual.

Quick quiz

Question 1

1.What is the main difference between hashing and encryption?

Question 2

2.Why does every user get their own random salt?

Question 3

3.Which is the first choice for hashing passwords in 2026?

Question 4

4.Why is plain SHA-256 a bad password hash?

Question 5

5.Why compare hashes in constant time?

Read more (free)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: Handling file uploads safely