Welcome to the "A04: Insecure Design" course! In this lesson, we will explore the topic of insecure credential recovery mechanisms, specifically focusing on the use of security questions and answers. Credential recovery is a critical component of web applications, allowing users to regain access to their accounts. However, when implemented insecurely, it can become a significant vulnerability.
Let's dive into understanding these mechanisms and how they can be exploited. 🚀
Security questions have been a popular authentication method since the early days of online banking and email services. The concept is simple: users choose predetermined questions and provide answers that only they should know. Common questions include "What's your mother's maiden name?", "What street did you grow up on?", or "What was your first pet's name?". While this method seemed secure initially, it has several fundamental flaws.
Other outdated recovery methods include:
- SMS-based recovery (vulnerable to SIM swapping)
- Simple PIN codes sent via email
- Birth date verification
- Last transaction amount (for banking)
The main security flaw with security questions is that the answers are often easily discoverable through social media, public records, or social engineering. This makes them particularly vulnerable to targeted attacks. Let's examine how these mechanisms typically work in code.
Credential recovery mechanisms are processes that allow users to regain access to their accounts when they forget their passwords. When implemented using security questions, the system compares user-provided answers against stored responses.
While this seems straightforward, it introduces several security risks because the answers are often predictable or publicly available information. Let's examine a vulnerable implementation that demonstrates these risks.
Let's examine a typical implementation of security question recovery, where the application first verifies the user exists and then checks their security answer. This is how the endpoint handles the initial recovery request:
This code immediately confirms whether a username exists in the system. Although hiding user existence provides maximum security, many applications choose to reveal this information but protect against enumeration attacks using CAPTCHA or rate limiting - so there is a trade-off that's worth exploring here.
Next, let's see how it handles the security answer verification:
This implementation is particularly vulnerable because it performs a simple string comparison and immediately returns a temporary password. Additionally, it lacks rate limiting, account lockouts, and stores security answers in plaintext, all of which are significant security issues. Now, let's see how an attacker might exploit these vulnerabilities.
Instead of relying on security questions, a more secure approach is to implement an email-based password reset system. This approach has become an industry standard used by major platforms like Google, Amazon, and Microsoft due to its robust security model.
The security of this method is multi-faceted and addresses several key vulnerabilities present in security question systems:
- Email accounts serve as a trusted secondary authentication channel, often protected by two-factor authentication (though not all users enable 2FA)
- Time-limited tokens ensure reset links expire quickly, typically within an hour
- The system generates cryptographically secure tokens that are practically impossible to guess
- Every reset attempt creates an audit trail through email and application logs
These security features work together to create a robust system that's both secure and user-friendly, while providing security teams the visibility they need to detect and respond to potential attacks.
Let's examine an example implementation of this secure solution step by step.
The reset token mechanism is a crucial part of secure password recovery. The resetToken is typically a cryptographically secure random string that serves as a one-time password reset link. When stored in the database, it's associated with the user's account and given an expiration timestamp.
This implementation creates a secure, time-limited token and sends it via email. The token acts like a temporary key - it must be unique, unpredictable, and should expire after a short period (in this case, one hour) to minimize the window of opportunity for potential attacks.
To securely generate and store password reset tokens, use the following implementation:
This implementation uses crypto.randomBytes() for generating unpredictable tokens and SHA-256 for secure token hashing, ensuring both the generation and storage of reset tokens are cryptographically secure. The token is sent to the user while the tokenHash is stored in the database, similar to how we handle passwords.
Note that SHA-256 is suitable for hashing tokens, but for passwords, a slow hash like bcrypt should be used.
In this lesson, we explored the vulnerabilities of using security questions for credential recovery and demonstrated how they can be exploited. We also discussed and implemented a secure email-based token system as a better alternative.
As you move forward, you'll have the opportunity to practice these concepts in the exercises that follow. In the next lesson, we'll continue to build on this foundation by exploring other common vulnerabilities and their mitigations. Keep up the great work! 🌟
