Juicy Shop
Cryptographic Failures
Sensitive data leaks when it is stored or transmitted without proper cryptography. Learn why you hash passwords (never encrypt them), how to pick algorithms, and where data-in-transit protection belongs.
Hashing vs encryption
Encryption is reversible — given the key you get the plaintext back. Hashing is one-way — you cannot recover the input from the digest. Passwords must be HASHED, never encrypted: the server never needs the original password back, it only needs to check whether a login attempt produces the same hash. Encrypting passwords is a finding because anyone with the key recovers every password at once.
Why fast hashes are wrong for passwords
MD5, SHA-1 and SHA-256 are designed to be fast, which is exactly what an attacker wants when brute-forcing a stolen hash table. Use a slow, salted, memory-hard password hash: Argon2id (preferred), scrypt, or bcrypt. A per-user random salt defeats rainbow tables, and a high work factor makes each guess expensive. Re-tune the work factor as hardware improves.
Data in transit and at rest
Protect data in transit with TLS everywhere — no mixed content, HSTS enabled, modern cipher suites only. Protect sensitive data at rest with authenticated encryption (AES-GCM or ChaCha20-Poly1305) and keys held in a KMS or secrets manager, never in source code. Classify data first: you cannot protect what you have not identified as sensitive.
Don't roll your own crypto
Cryptographic primitives are easy to misuse: a reused nonce, an ECB-mode cipher, or a non-constant-time comparison can silently destroy the guarantees. Use vetted, high-level libraries (libsodium, the platform WebCrypto API) that expose safe defaults, and let them manage IVs, padding, and authentication tags for you.
Insecure vs secure
import { createHash } from "node:crypto";
// Fast, unsalted hash — trivial to crack offline
const hash = createHash("sha256")
.update(password)
.digest("hex");
await db.insert({ email, password: hash });SHA-256 is fast and unsalted, so a leaked table can be cracked with rainbow tables or GPU brute force in minutes.
import { hash, verify } from "@node-rs/argon2";
// Slow, salted, memory-hard — Argon2id
const digest = await hash(password);
await db.insert({ email, password: digest });
// On login:
const ok = await verify(digest, attempt);Argon2id salts automatically and is memory-hard, so each guess is expensive and rainbow tables are useless.
Key takeaways
- Hash passwords (one-way); never encrypt them.
- Use Argon2id/scrypt/bcrypt with a per-user salt and a tuned work factor.
- Fast hashes (MD5/SHA-256) are wrong for passwords.
- Encrypt sensitive data in transit (TLS) and at rest (AES-GCM) with managed keys.
- Use vetted libraries instead of hand-rolled crypto.
Knowledge check
Practice
Apply this lesson hands-on in the matching find-it / fix-it challenge.
Open challenge →