用戶交互流程
- 用戶進入「忘記密碼」頁面
- 輸入註冊郵箱,提交請求
- 收到含重設鏈接的郵件(鏈接帶有效期)
- 點擊鏈接進入重設頁面,輸入新密碼完成
後端核心邏輯(無需額外數據表)
// 服務端唯一密鑰,需妥善保管
$KEY = "something really long long long long long and secret";
// 1. 生成重設鏈接
$email = $_POST['email'];
$user = get_user_by_email($email); // 自定義函數:查詢用戶
if ($user && $user['id']) {
$time = time();
$hash = md5($user['id'] . $time . $KEY);
$url = "https://domain.com/reset-password-form.php?id={$user['id']}×tamp=$time&hash=$hash";
send_email($email, '重設密碼', "請點擊鏈接重設密碼: $url");
}
驗證與重設頁面邏輯
$KEY = "something really long long long long long and secret";
$hash = $_GET['hash'];
$user_id = $_GET['id'];
$timestamp = $_GET['timestamp'];
// 驗證簽名
if ($hash !== md5($user_id . $timestamp . $KEY)) {
die('invalid parameters');
}
// 驗證過期(1小時)
if (time() - $timestamp > 3600) {
die('link expired');
}
// 驗證通過,顯示重設表單或處理提交
if ($_POST['new_password']) {
reset_user_password($user_id, $_POST['new_password']);
die('password changed successfully');
} else {
echo '<form method="post" action="reset-password-form.php?hash=$hash&id=$user_id×tamp=$timestamp">';
echo 'new password: <input type="password" name="new_password">';
echo '<input type="submit" value="submit"></form>';
}
方案優勢分析
| 優勢 | 說明 |
| 無需額外表 | 不存 token,通過簽名驗證,節省存儲 |
| 防篡改 | 參數經 MD5(user_id+timestamp+KEY) 簽名,竄改即驗證失敗 |
| 自帶時效 | timestamp 內嵌,服務端控制過期時間 |
| 高安全性 | KEY 足夠長且僅存服務端,簽名不可逆 |
安全加固建議
- 使用
hash_hmac('sha256', ...) 替代 MD5,防碰撞攻擊
- 強制 HTTPS 傳輸重設鏈接
- 限制重設請求頻率(如每小時最多 3 次)
- 重設後使所有現有 session 失效
- 記錄審計日誌:IP、時間、用戶 ID、操作結果