萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP 登入系統實作:Session 驗證、Cookie 記住我、密碼雜湊與安全防護

完整登入流程實作

<?php
session_start();  // 必須在任何輸出前呼叫

// 設定:僅 HTTPS 傳輸 Cookie,防 XSS
ini_set('session.cookie_secure', '1');
ini_set('session.cookie_httponly', '1');
ini_set('session.use_strict_mode', '1');

// 資料庫連線 (PDO)
$pdo = new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

$errors = [];
$redirectUrl = '/dashboard.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 1. CSRF 保護
    if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf_token'] ?? '')) {
        $errors[] = '無效的請求,請重新整理頁面';
    }
    
    // 2. 輸入驗證
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';
    $remember = isset($_POST['remember']);
    
    if ($username === '') $errors[] = '請輸入帳號';
    if ($password === '') $errors[] = '請輸入密碼';
    
    if (!$errors) {
        // 3. 查詢使用者
        $stmt = $pdo->prepare('SELECT id, username, password_hash, status, failed_login_count, locked_until FROM users WHERE username = ?');
        $stmt->execute([$username]);
        $user = $stmt->fetch();
        
        if (!$user) {
            $errors[] = '帳號或密碼錯誤';  // 統一訊息防止用戶枚舉
        } elseif ($user['status'] !== 1) {
            $errors[] = '帳號已停用,請聯繫管理員';
        } elseif ($user['locked_until'] && $user['locked_until'] > time()) {
            $errors[] = '帳號暫時鎖定,請稍後再試';
        } elseif (!password_verify($password, $user['password_hash'])) {
            // 4. 密碼錯誤:記錄失敗次數、鎖定機制
            $pdo->prepare('UPDATE users SET failed_login_count = failed_login_count + 1, locked_until = CASE WHEN failed_login_count >= 4 THEN ? ELSE NULL END WHERE id = ?')
                ->execute([time() + 900, $user['id']]);  // 鎖定 15 分鐘
            $errors[] = '帳號或密碼錯誤';
        } else {
            // 5. 登入成功
            $pdo->prepare('UPDATE users SET failed_login_count = 0, locked_until = NULL, last_login_at = NOW() WHERE id = ?')
                ->execute([$user['id']]);
            
            // 重新產生 Session ID 防 Session Fixation
            session_regenerate_id(true);
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['username'] = $user['username'];
            $_SESSION['login_time'] = time();
            
            // 6. 記住我:長效 Token (不直接存密碼)
            if ($remember) {
                $selector = bin2hex(random_bytes(16));
                $validator = bin2hex(random_bytes(32));
                $hashedValidator = hash('sha256', $validator);
                $expires = time() + 86400 * 30;  // 30 天
                
                $pdo->prepare('INSERT INTO remember_tokens (selector, validator_hash, user_id, expires) VALUES (?, ?, ?, ?)')
                    ->execute([$selector, $hashedValidator, $user['id'], $expires]);
                
                setcookie('remember_token', $selector . ':' . $validator, [
                    'expires' => $expires,
                    'path' => '/',
                    'secure' => true,
                    'httponly' => true,
                    'samesite' => 'Lax',
                ]);
            }
            
            header('Location: ' . $redirectUrl);
            exit;
        }
    }
}

// 產生 CSRF Token
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
?>


登入

    
        
$e
"; ?>

關鍵安全機制說明

機制實作方式防禦目標
密碼雜湊password_hash() + PASSWORD_ARGON2ID資料庫洩露時密碼不可逆
參數化查詢PDO Prepared StatementsSQL Injection
CSRF TokenSession + 表單隱藏欄位 + hash_equals()跨站請求偽造
Session 安全session_regenerate_id(true)、Secure/HttpOnly/SameSiteSession Fixation、XSS 竊取
暴力破解防護失敗計數 + 暫時鎖定 (15分鐘)密碼噴灑、暴力破解
用戶枚舉防護統一錯誤訊息「帳號或密碼錯誤」帳號存在性探測
記住我 TokenSelector:Validator 分離、Validator 雜湊儲存、HttpOnly Cookie長期 Token 洩露風險

記住我 Token 設計細節

  • Selector:公開識別碼,用於資料庫查找(可明文傳輸)。
  • Validator:秘密驗證碼,僅儲存 SHA-256 雜湊,驗證時比對雜湊。
  • Token 格式:selector:validator 存於 Cookie。
  • 驗證流程:Cookie → 解析 selector → 查資料庫 → hash_equals(hash('sha256', validator), db_hash)。
  • 登出/密碼變更時:刪除該使用者所有 remember_tokens。

登出實作

<?php
session_start();
// 刪除記住我 Token
if (isset($_COOKIE['remember_token'])) {
    [$selector] = explode(':', $_COOKIE['remember_token'], 2);
    $pdo->prepare('DELETE FROM remember_tokens WHERE selector = ?')->execute([$selector]);
    setcookie('remember_token', '', ['expires' => time() - 3600, 'path' => '/', 'secure' => true, 'httponly' => true]);
}

// 銷毀 Session
$_SESSION = [];
session_destroy();
header('Location: /login.php');
exit;
?>

這套登入系統涵蓋現代 Web 應用的核心安全需求,可作為專案起始模板直接擴充。

copyright © 萬盛學電腦網 all rights reserved