is_active=1)、清空 Token、提示成功CREATE TABLE `user` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`fname` VARCHAR(255) DEFAULT '',
`lname` VARCHAR(255) DEFAULT '',
`email` VARCHAR(100) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL, -- 存儲 bcrypt 哈希
`is_active` TINYINT(1) DEFAULT 0, -- 0 未激活,1 激活
`verify_token` VARCHAR(64) DEFAULT NULL, -- 32 字節十六進制 = 64 字符
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_token` (`verify_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
public function register() {
// 1. 驗證規則
$rules = [
'fname' => 'required|max:255',
'lname' => 'required|max:255',
'email' => 'required|email|unique:user,email',
'password' => 'required|min:8|max:72|confirmed', // confirmed 對應 password_confirmation
];
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::back()->withErrors($validator)->withInput();
}
// 2. 創建用戶(密碼哈希)
$user = new User();
$user->fname = Input::get('fname');
$user->lname = Input::get('lname');
$user->email = Input::get('email');
$user->password = Hash::make(Input::get('password')); // Laravel Hash::make() = bcrypt
// 3. 生成驗證 Token(32 字節隨機 = 256 位熵值,極難暴力破解)
$user->verify_token = Str::random(32);
if (!$user->save()) {
return Redirect::back()->with('error', '系統繁忙,請稍後重試')->withInput();
}
// 4. 發送驗證郵件(異步隊列推薦)
Mail::send('emails.verify', ['code' => $user->verify_token, 'id' => $user->id], function($m) use ($user) {
$m->from('noreply@mydomain.com', 'MyDomain')
->to($user->email, $user->fname . ' ' . $user->lname)
->subject('MyDomain:郵箱驗證');
});
return Redirect::to('register')->with('success', '註冊成功!驗證郵件已發送,請查收並點擊鏈接激活賬戶(48小時內有效)。');
}
emails/verify.blade.php<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body>
<p>請點擊下方鏈接完成郵箱驗證:</p>
<p><a href="https://mydomain.com/verify?code={{ $code }}&user={{ $id }}">
https://mydomain.com/verify?code={{ $code }}&user={{ $id }}
</a></p>
<p>鏈接 48 小時內有效。若非本人操作,請忽略。</p>
</body>
</html>
public function verify() {
$code = Input::get('code');
$id = Input::get('user');
// 1. 查詢:未激活 + Token 匹配 + 48小時內創建
$user = User::where('id', $id)
->where('is_active', 0)
->where('verify_token', $code)
->where('created_at', '>=', Carbon::now()->subHours(48))
->first();
if (!$user) {
return Redirect::to('verify')->with('error', '驗證鏈接無效或已過期');
}
// 2. 激活賬戶
$user->verify_token = null; // 一次性 Token,用完即銷毀
$user->is_active = 1;
if (!$user->save()) {
return Redirect::to('verify')->with('error', '激活失敗,請稍後重試');
}
return Redirect::to('login')->with('success', '賬戶激活成功!請登錄。');
}
| 風險點 | 防範措施 |
|---|---|
| Token 被猜測 | 32 字節隨機字符串(256 位熵),空間極大 |
| Token 永久有效 | 設置過期時間(示例 48h),過期自動失效 |
| 遍歷攻擊 | Token + User ID 雙重校驗,避免單一 Token 撞庫 |
| 郵件被劫持 | HTTPS 鏈接、HSTS、郵件 DKIM/SPF/DMARC |
| 數據庫洩露 | 密碼 bcrypt、Token 可選加密存儲 |
| 重複發送垃圾郵件 | 同郵箱/同 IP 限流、圖形驗證碼 |
Mail::send 推入隊列,響應毫秒級is_active=0 AND created_at < 48h 的垃圾賬戶本示例基於 Laravel 框架,核心邏輯可無縫遷移至 ThinkPHP、Yii、CodeIgniter 或原生 PHP 項目。