Retrieve password logic: integrate Flask-Mail to send verification emails
📂 Stage: Stage 3 - User System (Security) 🔗 Related chapters: Flask-Login 实战 · 密码安全加密
Why is it necessary to design like this? First sort out the overall process
Password retrieval is a "back-up security feature" of the user system. It must be simple enough to allow users to quickly reset their passwords; at the same time, it must be rigorous enough to prevent issues such as mailbox enumeration attacks and Token abuse. The picture below summarizes the complete link we want to implement, with each step being securely designed:
There are two "counter-intuitive" points to the entire process:
- Regardless of whether the email address is registered or not, the same prompt is given to the user to avoid leaking registration information.
- Token is one-time, with expiration time and exclusive "salt" to prevent Tokens from borrowing each other in different scenarios.
Install dependencies
We only need two third-party libraries:
- flask-mail: Helps you send emails with a few lines of code.
- itsdangerous: Generates an encrypted, timestamped security token, which Flask's built-in signature mechanism is based on.
One-click installation:
Step 1: Initialize Flask-Mail and global configuration
🛡️ Security reminder: In a production environment, never hardcode your email password into the code. used here
.envDocument coordinationpython-dotenvLoad sensitive information.
Create global extension object
To avoid circular imports, we useapp/extensions.pyThere is only one declaration inMailInstance, wait until the factory function creates the app before binding it.
Configure the mail service in the factory function
Take Gmail as an example (you need to turn on "Application-specific password"), other email services are similar, you only need to modifyMAIL_SERVERand port.
MAIL_DEFAULT_SENDERIt is a tuple, representing the sender's name and email address respectively, so that the received email will display "Daoman Blog" instead of a naked mailbox.
Step 2: Design a secure Token manager
Password reset requires a one-time, limited-time, user-specific credential.itsdangerousofURLSafeTimedSerializerJust meet these three points:
- LIMITED TIME: PASS
max_ageParameters automatically determine whether they have expired. - Bind mailbox: Encode the mailbox as data and directly decrypt it when using it.
- Scene Isolation: use different
salt(Salt) Distinguish between different services such as "password reset" and "email verification" to prevent mixed use of tokens.
We encapsulate Token related logic into a class to facilitate global calls:
BadDataIt can catch almost all exceptions and avoid distinguishing "signature error" and "timeout expiration" ourselves, which is very convenient.
Step 3: Write business routing
3.1 Forgot password - Submit email and send email
The most important security measure in this step is: Always give the user a consistent prompt, regardless of whether the email address is registered or not. This prevents attackers from enumerating our user list by submitting different email addresses and observing page feedback or response times.
💡 Tips:
url_for(..., _external=True)Complete domain names are automatically completed (e.g.http://localhost:5000/...), in the production environment as long as yourSERVER_NAMEIf configured correctly, the link can be used directly.
3.2 Reset password - verify Token and update password
After the user clicks the email link, they will enter this route, first verify the token, and then display the form for setting a new password. After submission, directly assign the plaintext password touser.password, automatically completing the hash using the setters we defined previously in the model.
Safety Points Review
Password retrieval is the “easiest to be targeted” link in the user system. Let’s compare this implementation to see what has been done correctly and which pitfalls must be avoided.
Production environment optimization suggestions
-
Switch to a professional email service provider Personal Gmail or self-built SMTP has a low delivery rate and is easily classified as spam. It is recommended to use SendGrid, Mailgun, Alibaba Cloud email push and other services.
-
Increase frequency limit For example, the same email address can be applied for at most 3 times in 1 hour, and the same IP can be applied for at most 10 times in 1 hour. You can use Redis to make a simple counter.
-
Record password reset log Record the time of each application, IP address, whether the reset is successful, etc. to facilitate subsequent troubleshooting of security issues.
-
Force old sessions to expire after reset You can clear the user's current
session_id, or call Flask-Login'slogout_user()And clear the session cache in Redis to ensure that the old login status is immediately invalidated.
Summary
Behind a seemingly simple "retrieve password" function, there are actually many security details hidden. We implemented it in four steps:
- Generate a salted, time-limited, URL-safe Token;
- Construct a reset link and use Flask-Mail to send an email containing both plain text and HTML content;
- Strictly verify the Token’s signature, salt and validity period after the user clicks;
- After the new password is submitted, it is automatically hashed and written into the database.
This process not only ensures the operating experience of ordinary users, but also effectively prevents most attacks on password retrieval.
🔗 Extended reading

