← Back to blog
安全密码重置事务邮件开发者
Secure Password Reset Emails
Cryptographically secure token generation, hash storage, expiry, rate limiting, and single-use enforcement.
Updated October 12, 2026

Secure Password Reset Emails
Password reset emails are one of the most targeted entry points for account takeovers. A weak implementation hands attackers the keys.
Generating and storing the token
Use a cryptographically secure random generator, at least 32 bytes, encoded as URL-safe Base64:
const crypto = require('crypto');
const token = crypto.randomBytes(32).toString('base64url');
Store the hash, not the plaintext token:
const hash = crypto.createHash('sha256').update(token).digest('hex');
await db.query(
'INSERT INTO password_resets (user_id, token_hash, expires_at) VALUES ($1, $2, NOW() + INTERVAL '15 minutes')',
[userId, hash]
);
Sending the email
await rovela.emails.send({
from: 'security@yourdomain.com',
to: user.email,
subject: 'Reset your password',
html: resetTemplate({ resetUrl: `https://yourdomain.com/reset-password?token=${token}` }),
text: `Reset your password (valid for 15 minutes): https://yourdomain.com/reset-password?token=${token}`,
});
Expiry and single use
- Expire after 15 minutes — require a new request after that
- Delete on use — remove the token from the database the moment it's redeemed
- Return "sent" even if the email doesn't exist — don't let attackers enumerate accounts
// Same response regardless of whether the account exists
res.json({ message: 'If that email is registered, you will receive a reset link.' });
Rate limiting
- Allow at most 3 requests per email per hour
- Log all requests with IP
- Send a notification email when the password is successfully changed
Link format
Use HTTPS. Put the token in a query parameter, not a URL hash — the hash is never sent to the server. Format: https://yourdomain.com/reset-password?token=<token>