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

PHP MySQL 登入驗證機制實作:檢查腳本與受保護頁面範例

     編輯:PHP 相關知識

架構設計

採用「檢查腳本 + 受保護頁面」模式:

  • login_chk.php:驗證 Session 合法性,失敗則阻擋
  • 各業務頁面 require() 此檔,自動獲得保護
  • 登入頁面 login.php 處理認證,成功寫入 Session

核心檢查腳本 (login_chk.php)

<?php
// login_chk.php
session_start();

// 檢查是否已登入 (假設登入成功設定 $_SESSION['authenticated'] = true)
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
    // 未登入:導向登入頁,記錄原本想去的頁面
    $redirect = urlencode($_SERVER['REQUEST_URI'] ?? '/');
    header("Location: login.php?redirect=$redirect");
    exit;
}

// 可選:檢查權限等級
// if ($_SESSION['role'] !== 'admin') { header('Location: no_permission.php'); exit; }

// 可選:Session 閒置逾時檢查 (30 分鐘)
$timeout = 1800;
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > $timeout)) {
    session_unset();
    session_destroy();
    header("Location: login.php?timeout=1");
    exit;
}
$_SESSION['last_activity'] = time();

// 可選:防 Session 劫持 (驗證 User-Agent + IP 前段)
$uaHash = md5($_SERVER['HTTP_USER_AGENT'] ?? '');
$ipPrefix = implode('.', array_slice(explode('.', $_SERVER['REMOTE_ADDR'] ?? ''), 0, 2));
if (($_SESSION['ua_hash'] ?? '') !== $uaHash || ($_SESSION['ip_prefix'] ?? '') !== $ipPrefix) {
    session_destroy();
    header("Location: login.php?hijack=1");
    exit;
}
?>

登入處理頁面 (login.php)

<?php
// login.php
session_start();
require_once 'db.php'; // PDO 連線

$errors = [];
$redirect = $_GET['redirect'] ?? '/dashboard.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';
    
    if ($username === '' || $password === '') {
        $errors[] = '帳號密碼不可為空';
    } else {
        // 查詢使用者 (預備語句防注入)
        $stmt = $pdo->prepare("SELECT id, username, password_hash, role FROM users WHERE username = ?");
        $stmt->execute([$username]);
        $user = $stmt->fetch();
        
        if ($user && password_verify($password, $user['password_hash'])) {
            // 登入成功
            session_regenerate_id(true); // 防 Session Fixation
            
            $_SESSION['authenticated'] = true;
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['username'] = $user['username'];
            $_SESSION['role'] = $user['role'];
            $_SESSION['last_activity'] = time();
            $_SESSION['ua_hash'] = md5($_SERVER['HTTP_USER_AGENT'] ?? '');
            $_SESSION['ip_prefix'] = implode('.', array_slice(explode('.', $_SERVER['REMOTE_ADDR'] ?? ''), 0, 2));
            
            // 導向原頁面
            header("Location: " . urldecode($redirect));
            exit;
        } else {
            $errors[] = '帳號或密碼錯誤';
        }
    }
}
?>
<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>登入系統</title>
    <style>.error { color: red; margin: 10px 0; }</style>
</head>
<body>
    <h1>會員登入</h1>
    
    <?php if ($errors): ?>
        <div class="error">
            <?php foreach ($errors as $e) echo "<p>" . htmlspecialchars($e) . "</p>"; ?>
        </div>
    <?php endif; ?>
    
    <form method="post">
        <input type="hidden" name="redirect" value="<?= htmlspecialchars($redirect) ?>">
        <p><label>帳號:<input type="text" name="username" required></label></p>
        <p><label>密碼:<input type="password" name="password" required></label></p>
        <p><button type="submit">登入</button></p>
    </form>
</body>
</html>

受保護頁面範例 (sample.php)

<?php
require_once 'login_chk.php'; // 一行即可獲得保護
?>
<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>會員專區</title>
</head>
<body>
    <h1>歡迎, <?= htmlspecialchars($_SESSION['username']) ?>!</h1>
    <p>您已通過驗證,可以存取此頁面。</p>
    <p>角色: <?= htmlspecialchars($_SESSION['role']) ?></p>
    <p><a href="logout.php">登出</a></p>
</body>
</html>

登出處理 (logout.php)

<?php
session_start();

// 完全銷毀 Session
$_SESSION = [];
if (ini_get("session.use_cookies")) {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
}
session_destroy();

header("Location: login.php?logout=1");
exit;?>

資料庫結構建議

CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,  -- password_hash() 產生
    role ENUM('user', 'admin', 'moderator') DEFAULT 'user',
    status ENUM('active', 'inactive', 'banned') DEFAULT 'active',
    email_verified_at TIMESTAMP NULL,
    last_login_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

密碼雜湊最佳實踐

<?php
// 註冊/修改密碼時
$passwordHash = password_hash($plainPassword, PASSWORD_DEFAULT); // 目前為 bcrypt
// 儲存 $passwordHash 到資料庫

// 驗證時
if (password_verify($plainPassword, $storedHash)) {
    // 成功
    // 檢查是否需重新雜湊 (演算法更新、cost 變更)
    if (password_needs_rehash($storedHash, PASSWORD_DEFAULT)) {
        $newHash = password_hash($plainPassword, PASSWORD_DEFAULT);
        // 更新資料庫
    }
}
?>

安全性增強清單

措施實作方式防護目標
HTTPS 強制Nginx/Apache 導向、HSTS 標頭傳輸加密、防竊聽
密碼強度要求前後端雙重驗證 (長度、複雜度)防暴力破解
登入失敗限流Redis 計數 IP+帳號、封鎖 15 分鐘防暴力破解、撞庫
雙因子認證 (2FA)TOTP (Google Authenticator) / SMS帳號被盜仍安全
CSRF Token表單隱藏欄位、驗證防跨站請求偽造
SameSite Cookiesetcookie(..., ['samesite' => 'Lax'])防 CSRF
安全標頭CSP、X-Frame-Options、Referrer-Policy防 XSS、點擊劫持
審計日誌記錄登入/登出/失敗/權限變更事後追蹤、合規

現代框架整合 (Laravel 範例)

<?php
// routes/web.php
Route::middleware('guest')->group(function () {
    Route::get('/login', [AuthController::class, 'show'])->name('login');
    Route::post('/login', [AuthController::class, 'login']);
});

Route::middleware('auth')->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
});

// app/Http/Controllers/AuthController.php
public function login(Request $request) {
    $credentials = $request->validate([
        'username' => 'required|string',
        'password' => 'required|string',
    ]);
    
    if (Auth::attempt($credentials, $request->boolean('remember'))) {
        $request->session()->regenerate();
        return redirect()->intended('/dashboard');
    }
    
    return back()->withErrors(['username' => '帳號或密碼錯誤']);
}
?>

登入驗證是 Web 應用安全的第一道防線。從手寫 login_chk.php 到框架內建 Auth,核心原則不變:嚴格驗證身分、安全管理 Session、最小權限存取、完整審計記錄。現代開發強烈建議使用框架成熟的認證系統,避免重複造輪子導致安全漏洞。

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