零框架、原生 PHP + MySQL 實作完整註冊/登入系統,涵蓋表單驗證、password_hash 密碼雜湊、Session 狀態管理、Prepared Statements 防 SQL 注入、XSS 防護,適合理解認證核心流程。
CREATE DATABASE auth_demo DEFAULT CHARSET utf8mb4;
USE auth_demo;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
db.php<?php
$mysqli = new mysqli('127.0.0.1', 'root', '', 'auth_demo');
if ($mysqli->connect_errno) die('DB 連線失敗');
$mysqli->set_charset('utf8mb4');
// 開啟嚴格模式
$mysqli->query("SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'");
?>
register.php<?php
require 'db.php';
session_start();
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$confirm = $_POST['confirm'] ?? '';
// 驗證
if (!$username || !$password) $errors[] = '帳號密碼不可為空';
if ($password !== $confirm) $errors[] = '兩次密碼不一致';
if (strlen($password) < 8) $errors[] = '密碼至少 8 字符';
if (!preg_match('/[A-Za-z]/', $password) || !preg_match('/\d/', $password)) {
$errors[] = '密碼需含字母與數字';
}
// 檢查用戶名是否存在
if (empty($errors)) {
$stmt = $mysqli->prepare('SELECT id FROM users WHERE username = ?');
$stmt->bind_param('s', $username);
$stmt->execute();
if ($stmt->get_result()->num_rows > 0) {
$errors[] = '帳號已被註冊';
}
$stmt->close();
}
// 寫入
if (empty($errors)) {
$hash = password_hash($password, PASSWORD_DEFAULT); // Bcrypt cost=10
$stmt = $mysqli->prepare('INSERT INTO users (username, password_hash) VALUES (?, ?)');
$stmt->bind_param('ss', $username, $hash);
if ($stmt->execute()) {
$_SESSION['success'] = '註冊成功,請登入';
header('Location: login.php');
exit;
} else {
$errors[] = '註冊失敗,請重試';
}
$stmt->close();
}
}
?>
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>註冊</title></head><body>
<h1>註冊</h1>
<?php if ($errors): ?><ul style="color:red"><?php foreach ($errors as $e) echo "login.php<?php
require 'db.php';
session_start();
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if ($username && $password) {
$stmt = $mysqli->prepare('SELECT id, password_hash FROM users WHERE username = ?');
$stmt->bind_param('s', $username);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();
$stmt->close();
if ($user && password_verify($password, $user['password_hash'])) {
// 登入成功,建立 Session
session_regenerate_id(true); // 防 Session Fixation
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $username;
header('Location: dashboard.php');
exit;
}
}
$error = '帳號或密碼錯誤';
}
?>
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>登入</title></head><body>
<h1>登入</h1>
<?php if ($error) echo "$error
"; ?>
<form method="post">
帳號: <input type="text" name="username" required><br><br>
密碼: <input type="password" name="password" required><br><br>
<button type="submit">登入</button>
</form>
<p><a href="register.php">註冊新帳號</a></p>
</body></html>
dashboard.php<?php
session_start();
if (empty($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
require 'db.php';
$stmt = $mysqli->prepare('SELECT username FROM users WHERE id = ?');
$stmt->bind_param('i', $_SESSION['user_id']);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();
?>
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>儀表板</title></head><body>
<h1>歡迎, <?= htmlspecialchars($user['username']) ?></h1>
<p><a href="logout.php">登出</a></p>
</body></html>
logout.php<?php
session_start();
$_SESSION = [];
session_destroy();
header('Location: login.php');
?>
password_hash(PASSWORD_DEFAULT) → Bcrypt,自動鹽值、Cost 可升級。prepare/bind_param,徹底防注入。session_regenerate_id(true) 登入後換 ID、session.cookie_httponly=1、session.cookie_secure=1 (HTTPS)。
4. XSS 防護:輸出變數用 htmlspecialchars()。
5. CSRF 保護:表單加入 $_SESSION['csrf'] = bin2hex(random_bytes(32)) 驗證(此處簡化省略)。
6. 暴力破解防禦:登入失敗計數 + 延遲/鎖定(可用 Redis 計數器)。
認證系統核心只有三步:雜湊存儲 → 驗證比對 → Session 維持。掌握 password_hash/verify、mysqli prepare、session 三大支柱,框架封裝的 Auth 組件才不會變成黑盒。