← 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

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>