session_start():啟動/恢復會話,發送 Set-Cookie: PHPSESSID=.../tmp/sess_*)$_SESSION 超全域陣列<?php
session_start(); // 必須在輸出前呼叫
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'tom';
$_SESSION['roles'] = ['user', 'editor'];
$_SESSION['last_activity'] = time();
?>
<?php
session_start();
if (isset($_SESSION['user_id'])) {
echo "歡迎,{$_SESSION['username']} (ID: {$_SESSION['user_id']})";
}
?>
<?php
// config.php
declare(strict_types=1);
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'myapp');
define('DB_USER', 'app_user');
define('DB_PASS', 'secure_password');
define('SESSION_LIFETIME', 1800); // 30 分鐘
// Session 安全設定 (必須在 session_start() 前)
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) ? '1' : '0');
ini_set('session.use_strict_mode', '1');
ini_set('session.gc_maxlifetime', (string)SESSION_LIFETIME);
// 可選:自定儲存處理器 (Redis/Database)
?>
<?php
// login.php
require_once 'config.php';
session_start();
$errors = [];
$redirect = $_GET['redirect'] ?? '/dashboard.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if ($username === '' || $password === '') {
$errors[] = '帳號密碼不可為空';
} else {
try {
$pdo = new PDO(
"mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4",
DB_USER, DB_PASS,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$stmt = $pdo->prepare('SELECT id, username, password_hash, roles FROM users WHERE username = ?');
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
// 登入成功:重新產生 Session ID (防 Session Fixation)
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['roles'] = json_decode($user['roles'], true) ?? ['user'];
$_SESSION['login_time'] = time();
$_SESSION['last_activity'] = time();
// 記住我功能
if (!empty($_POST['remember'])) {
$selector = bin2hex(random_bytes(16));
$validator = bin2hex(random_bytes(32));
$hash = hash('sha256', $validator);
$expires = time() + 30*86400;
$pdo->prepare('INSERT INTO remember_tokens (selector, validator_hash, user_id, expires) VALUES (?, ?, ?, ?)')
->execute([$selector, $hash, $user['id'], date('Y-m-d H:i:s', $expires)]);
setcookie('remember_me', $selector . ':' . $validator, $expires, '/', '', true, true);
}
header('Location: ' . $redirect);
exit;
}
$errors[] = '帳號或密碼錯誤';
} catch (PDOException $e) {
error_log('Login error: ' . $e->getMessage());
$errors[] = '系統錯誤,請稍後再試';
}
}
}
?>
登入
會員登入
" . htmlspecialchars($e) . ""; ?>
<?php
// dashboard.php
require_once 'config.php';
session_start();
// 檢查登入狀態
if (empty($_SESSION['user_id'])) {
header('Location: login.php?redirect=' . urlencode($_SERVER['REQUEST_URI']));
exit;
}
// 檢查 Session 逾時
if (time() - $_SESSION['last_activity'] > SESSION_LIFETIME) {
session_destroy();
header('Location: login.php?expired=1');
exit;
}
$_SESSION['last_activity'] = time();
// 角色授權檢查
function hasRole(string $role): bool {
return in_array($role, $_SESSION['roles'] ?? [], true);
}
?>
會員中心
歡迎,= htmlspecialchars($_SESSION['username']) ?>
角色: = implode(', ', $_SESSION['roles']) ?>
<?php
// logout.php
require_once 'config.php';
session_start();
// 刪除記住我 Token
if (isset($_COOKIE['remember_me'])) {
[$selector] = explode(':', $_COOKIE['remember_me'], 2);
$pdo = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS);
$pdo->prepare('DELETE FROM remember_tokens WHERE selector = ?')->execute([$selector]);
setcookie('remember_me', '', time() - 3600, '/', '', true, true);
}
// 銷毀 Session
$_SESSION = [];
session_destroy();
header('Location: login.php?logged_out=1');
exit;
?>
session_regenerate_id(true) 登入後重新產生 IDsession.use_strict_mode=1 拒絕未初始化的 Session IDcookie_httponly=1 防 XSS 竊取 Session IDcookie_secure=1 (HTTPS 環境) 只傳輸加密 CookieSameSite=Strict/Lax 防 CSRF (PHP 7.3+ session_set_cookie_params())prepare('SELECT user_id, validator_hash, expires FROM remember_tokens WHERE selector = ?'); $stmt->execute([$selector]); $token = $stmt->fetch(); if ($token && $token['expires'] > date('Y-m-d H:i:s') && hash_equals($token['validator_hash'], hash('sha256', $validator))) { // 取得使用者資料 $stmt = $pdo->prepare('SELECT id, username, roles FROM users WHERE id = ?'); $stmt->execute([$token['user_id']]); $user = $stmt->fetch(); if ($user) { session_regenerate_id(true); $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username']; $_SESSION['roles'] = json_decode($user['roles'], true) ?? ['user']; $_SESSION['login_time'] = time(); $_SESSION['last_activity'] = time(); // 旋轉 Token (安全最佳實務) $newSelector = bin2hex(random_bytes(16)); $newValidator = bin2hex(random_bytes(32)); $newHash = hash('sha256', $newValidator); $pdo->prepare('UPDATE remember_tokens SET selector = ?, validator_hash = ? WHERE selector = ?') ->execute([$newSelector, $newHash, $selector]); setcookie('remember_me', $newSelector . ':' . $newValidator, strtotime($token['expires']), '/', '', true, true); } } } }