Untitled Lesson
Skill Matrix
UNLOCK NODES BY LEARNING NEW TAGS.
Why must a TOTP secret be stored encrypted (reversibly), rather than hashed the way a password is stored?
💻 Code Challenge | +75 XP
Implement TOTP-based MFA enrollment (generating a secret, displaying a QR code URI, requiring one valid code before enabling) and a rate-limited verification endpoint with a clock-drift tolerance window.
A security review found that an MFA verification endpoint had no rate limiting, making a 6-digit TOTP code theoretically brute-forceable given enough attempts, and MFA secrets were stored in plaintext in the database. Reorder the steps to fix both issues.
Task: Reorder the blocks in logical sequence to solve the problem.
A.D.A. Interface
Adaptive Didactic Assistant

Pascual Vila
Frontend Instructor // Code Syllabus
The Error //
Storing a user's TOTP secret in plaintext in the database
// Wrong: plaintext secret, fully exposed if the database leaks
await db.users.update(userId, { mfaSecret: secret });
// Correct: encrypted at rest
const encryptedSecret = encrypt(secret, ENCRYPTION_KEY);
await db.users.update(userId, { mfaSecret: encryptedSecret });The Solution //
The TOTP secret is functionally equivalent to a permanent second password — if the database is ever compromised or leaked, an attacker with the plaintext secret can generate valid MFA codes indefinitely, completely defeating the purpose of the second factor. Encrypt the secret at rest, with the encryption key managed and stored separately.
The Error //
Having no rate limiting on the MFA code verification endpoint
// Wrong: unlimited verification attempts, brute-forceable
app.post("/auth/mfa/verify", verifyMfaHandler);
// Correct: strict rate limiting closes the brute-force gap
app.post("/auth/mfa/verify", rateLimit({ windowMs: 900000, max: 5 }), verifyMfaHandler);The Solution //
A 6-digit TOTP code has only 1,000,000 possible values — without rate limiting on verification attempts, an attacker who already has a valid stolen password could feasibly brute-force the correct MFA code through repeated attempts, defeating MFA's protective purpose entirely.