On this page
  1. Hashing vs encryption
  2. Why the algorithm matters
  3. How to do it
  4. Where AI coding assistants get this wrong
  5. Checklist
  6. FAQ
    1. What is a salt?
    2. Why not just use SHA-256?
    3. Is encryption ever OK for passwords?
  7. Related topics
  8. Sources
tutorial

How to Store Passwords Correctly (Hashing vs Encryption)

Passwords must be hashed with a slow algorithm like Argon2 or bcrypt, never encrypted or stored in plaintext. Learn why and how, with code examples.

Quick answer

  • Passwords must be hashed, not encrypted: hashing is one-way, so a stolen database doesn’t reveal passwords.
  • Use a slow, salted algorithm — Argon2id, bcrypt, or scrypt — never fast hashes like MD5 or SHA-1.
  • Encryption is reversible and therefore wrong for passwords; it’s one of the most common AI-assistant mistakes.

Hashing vs encryption

Encryption is two-way: encrypt a value, and you can decrypt it back. That’s correct for data you need to read later, but wrong for passwords — if you can reverse it, so can an attacker with the database. Hashing is one-way: a hash function maps input to a fixed output that can’t be reversed. You store the hash, and at login you hash the attempt and compare. This is why “we encrypt passwords” is always a red flag.

Why the algorithm matters

Fast general-purpose hashes (MD5, SHA-1, even plain SHA-256) are wrong for passwords because attackers can test billions of guesses per second. Password hashes must be slow and salted. A salt is a random value added per user so identical passwords don’t produce identical hashes; slowness makes brute force expensive. Argon2id is the current recommendation, with bcrypt and scrypt as acceptable alternatives.

How to do it

In Node.js with bcrypt:

const bcrypt = require("bcrypt");

// Store: hash the password with a salt and cost factor
const hash = await bcrypt.hash(password, 12);
await db.users.insert({ email, password_hash: hash });

// Verify at login
const ok = await bcrypt.compare(attempt, user.password_hash);

In Python with Argon2:

from argon2 import PasswordHasher
ph = PasswordHasher()

hashed = ph.hash(password)      # store this
ph.verify(hashed, attempt)      # raises on mismatch

How to verify it worked: the stored value looks like a long random string, never contains the password, and compare/verify succeeds only for the correct password.

Where this bites vibecoders

This is a documented, specific failure: AI assistants regularly implement reversible “encryption” for passwords instead of hashing, because “store it securely” can be satisfied syntactically by encryption. The check is simple — if your code can turn a stored value back into a password, it’s wrong. Hash, salt, and slow the algorithm; anything else is a breach waiting to be reported.

Where AI coding assistants get this wrong

  • Using reversible encryption (AES) or base64 and calling it “secure.”
  • Choosing MD5 or SHA-256 for password hashing.
  • Hashing without a per-user salt.
  • Comparing passwords with == after decrypting instead of hashing the attempt.

Checklist

  • Hash passwords, never encrypt or store plaintext.
  • Use Argon2id, bcrypt, or scrypt with a strong cost factor.
  • Salt every password with a unique random value.
  • Compare hashes using the library’s constant-time verify function.
  • Never log passwords or return them in API responses.

FAQ

What is a salt?

A salt is a unique random value combined with each password before hashing. It ensures two users with the same password get different hashes and defeats precomputed “rainbow table” attacks. Modern libraries add salts automatically.

Why not just use SHA-256?

SHA-256 is designed to be fast, which is the opposite of what passwords need. Attackers use that speed to brute-force billions of guesses. Password algorithms are deliberately slow to make each guess expensive.

Is encryption ever OK for passwords?

No — for stored login credentials, hashing is the correct primitive because it’s one-way. Encryption is appropriate for data you legitimately need to decrypt, like API tokens you must present back, but not for passwords.

Sources

Share: