首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 會員找回密碼功能完整實作:Token 驗證、郵件發送與重置流程

     編輯:PHP 相關知識

PHP 會員找回密碼功能完整實作

透過註冊信箱發送重置連結,使用一次性 Token 驗證身分,確保流程安全可控。以下為核心流程與關鍵代碼。

資料表設計

CREATE TABLE `users` (
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `username` VARCHAR(50) NOT NULL UNIQUE,
  `password` CHAR(60) NOT NULL, -- bcrypt hash
  `email` VARCHAR(100) NOT NULL UNIQUE,
  `reset_token` CHAR(64) DEFAULT NULL, -- SHA256 hex
  `reset_expires` INT UNSIGNED DEFAULT NULL, -- Unix timestamp
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

流程概覽

  1. 使用者輸入註冊信箱 → 前端 AJAX 提交。
  2. 後端驗證信箱存在 → 產生 Token、設定過期時間(如 1 小時) → 寫入 DB → 發送重置郵件。
  3. 使用者點擊郵件連結 → 進入重置頁面 → 後端驗證 Token 有效且未過期 → 顯示設定新密碼表單。
  4. 提交新密碼 → 後端再次驗證 Token → 更新密碼、清除 Token → 導向登入頁。

關鍵後端代碼片段

1. 申請重置 (forgot.php)

<?php
require 'db.php'; // PDO 連線
$email = trim($_POST['email'] ?? '');
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    exit(json_encode(['status'=>'error','msg'=>'信箱格式錯誤']));
}

$stmt = $pdo->prepare('SELECT id FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
if (!$user) {
    // 為防止信箱枚舉,前端統一顯示「若信箱存在,重置郵件已發送」
    exit(json_encode(['status'=>'ok','msg'=>'若信箱存在,重置郵件已發送']));
}

// 產生安全隨機 Token
$token = bin2hex(random_bytes(32)); // 64 字元 hex
$expires = time() + 3600; // 1 小時後過期

$stmt = $pdo->prepare('UPDATE users SET reset_token = ?, reset_expires = ? WHERE id = ?');
$stmt->execute([$token, $expires, $user['id']]);

// 發送郵件 (使用 PHPMailer 或 Symfony Mailer)
$resetLink = "https://example.com/reset.php?token=$token";
$mailBody = "請點擊以下連結重置密碼(1小時內有效):
$resetLink"; // send_mail($email, '密碼重置', $mailBody); // 實作省略 exit(json_encode(['status'=>'ok','msg'=>'若信箱存在,重置郵件已發送'])); ?>

2. 驗證 Token 與顯示重置表單 (reset.php)

<?php
require 'db.php';
$token = $_GET['token'] ?? '';
if (strlen($token) !== 64 || !ctype_xdigit($token)) {
    exit('無效的重置連結');
}

$stmt = $pdo->prepare('SELECT id, reset_expires FROM users WHERE reset_token = ?');
$stmt->execute([$token]);
$user = $stmt->fetch();

if (!$user) {
    exit('無效的重置連結');
}
if ($user['reset_expires'] < time()) {
    exit('重置連結已過期,請重新申請');
}

// Token 有效,顯示重置表單(隱藏 token 欄位)
?>


設定新密碼



3. 處理密碼更新 (reset_submit.php)

<?php
require 'db.php';
$token = $_POST['token'] ?? '';
$pass = $_POST['password'] ?? '';
$pass2 = $_POST['password_confirm'] ?? '';

if ($pass !== $pass2) { exit('兩次密碼不一致'); }
if (strlen($pass) < 8) { exit('密碼至少 8 碼'); }

$stmt = $pdo->prepare('SELECT id FROM users WHERE reset_token = ? AND reset_expires > ?');
$stmt->execute([$token, time()]);
$user = $stmt->fetch();
if (!$user) { exit('Token 無效或已過期'); }

$hash = password_hash($pass, PASSWORD_BCRYPT);
$stmt = $pdo->prepare('UPDATE users SET password = ?, reset_token = NULL, reset_expires = NULL WHERE id = ?');
$stmt->execute([$hash, $user['id']]);

header('Location: login.php?reset=success');
exit;
?>

安全性關鍵點

  • Token 使用加密安全隨機源:random_bytes(),不可用 md5()、uniqid() 等可預測值。
  • Token 單次使用、設定過期:驗證成功後立即清除 DB 中的 Token。
  • 防止信箱枚舉:無論信箱是否存在,前端回饋一致訊息。
  • 密碼雜湊:使用 password_hash() (BCRYPT/ARGON2ID),嚴禁明文或 MD5/SHA1。
  • HTTPS 全程強制:重置連結、表單提交均需 HTTPS。
  • 限流:申請重置介面加入 CAPTCHA 或 IP/信箱頻率限制。

此流程符合 OWASP 認證重置最佳實踐,可直接套用於實際專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved