TL;DR
Don’t tell users if their email address isn’t registered when they request a password reset. A generic message like ‘If an account exists with that email, you will receive instructions’ is much safer.
Why it matters
A standard “forgot password” form can be exploited to enumerate valid email addresses on your system. If the form explicitly states whether an email address is found or not, attackers can systematically test lists of emails to identify registered users. This is a common reconnaissance step before larger attacks.
How to fix it
- Change the Response Message: The most important step. Instead of saying “Email not found” or similar, use a generic message.
- Good: ‘If an account exists with that email address, you will receive password reset instructions shortly.’
- Bad: ‘Invalid email address’ or ‘No account found with that email.’
- Server-Side Logic: Ensure your server always responds in the same way regardless of whether the email exists.
Your backend code should not differentiate its response based on email validity. The logic to send an email (or not) should happen internally.
# Example Python/Flask from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/forgot_password', methods=['POST']) def forgot_password(): email = request.form['email'] # Always return a success message. return jsonify({'message': 'If an account exists with that email, you will receive instructions.'}) - Rate Limiting: Implement rate limiting on the forgot password endpoint to prevent brute-force attacks.
Limit the number of requests from a single IP address within a specific timeframe. This makes it harder for attackers to test many email addresses quickly.
# Example using Flask-Limiter from flask_limiter import Limiter app = Flask(__name__) limiter = Limiter(app, default_limit=5, per='hour') @app.route('/forgot_password', methods=['POST'], endpoint='forgot_password') @limiter.limit def forgot_password(): # ... your code here ... - Consider CAPTCHA: For high-risk applications, add a CAPTCHA to the forgot password form.
This helps prevent automated attacks.
- Account Lockout: Implement account lockout after multiple failed login or password reset attempts.
This further mitigates brute-force attacks.
Testing
- Test with a valid email address. Verify you receive the password reset instructions.
- Test with an invalid email address. Ensure you get the generic message, not an error indicating the email doesn’t exist.
- Attempt to submit multiple requests for both valid and invalid emails in quick succession (to test rate limiting).