註冊系統中檢查使用者名稱是否已存在是基本功能,但直接拼接 SQL 字串極易造成 SQL Injection。本文展示正確做法:使用 MySQLi Prepared Statement 進行參數化查詢,並修正原文中的邏輯錯誤(如 mysqli_num_rows 用法、變數拼寫)。
$_REQUEST['username'] 拼入 SQL → SQL Injection 風險極高。mysql_num_rows($result) 是舊 mysql_* 函式,與 mysqli 混用會報錯。$username / $password 未過濾、未驗證。<?php
// 1. 取得並基本驗證輸入
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if ($username === '') {
exit('使用者名稱不可為空');
}
// 2. 建立連線(建議改用 PDO,此處示範 MySQLi)
$mysqli = new mysqli('localhost', 'root', '', 'my_db');
if ($mysqli->connect_errno) {
exit('資料庫連線失敗: ' . $mysqli->connect_error);
}
$mysqli->set_charset('utf8mb4');
// 3. 檢查使用者是否存在(Prepared Statement)
$stmt = $mysqli->prepare('SELECT id FROM `user` WHERE `username` = ? LIMIT 1');
$stmt->bind_param('s', $username);
$stmt->execute();
$stmt->store_result(); // 重要:取得行數前必須呼叫
if ($stmt->num_rows > 0) {
echo '此使用者名稱已存在';
$stmt->close();
$mysqli->close();
exit;
}
$stmt->close();
// 4. 插入新使用者(密碼務必 hash!)
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $mysqli->prepare('INSERT INTO `user` (`username`, `password`) VALUES (?, ?)');
$stmt->bind_param('ss', $username, $passwordHash);
if ($stmt->execute()) {
echo '註冊成功,新增 1 筆資料';
} else {
echo '錯誤: ' . $stmt->error;
}
$stmt->close();
$mysqli->close();
?>
| 項目 | 錯誤做法 | 正確做法 |
|---|---|---|
| SQL 參數 | 字串拼接 'WHERE username=\"$username\"' | Prepared Statement WHERE username = ? + bind_param |
| 密碼儲存 | 明文儲存 | password_hash() + password_verify() |
| 字元集 | 預設 latin1 | set_charset('utf8mb4') |
| 錯誤處理 | 直接 die(mysql_error()) 洩漏結構 | 記錄日誌、回傳通用訊息 |
| 輸入驗證 | 無 | 長度、格式、唯一性驗證 |
<?php
$pdo = new PDO('mysql:host=localhost;dbname=my_db;charset=utf8mb4', 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 真正的預處理
]);
$stmt = $pdo->prepare('SELECT id FROM `user` WHERE `username` = ?');
$stmt->execute([$username]);
if ($stmt->fetch()) {
exit('使用者名稱已存在');
}
$hash = password_hash($password, PASSWORD_DEFAULT);
$pdo->prepare('INSERT INTO `user` (`username`, `password`) VALUES (?, ?)')
->execute([$username, $hash]);
echo '註冊成功';
?>
PDO 程式碼更簡潔、支援多資料庫、預處理預設為真正預處理(EMULATE_PREPARES=false),是現代 PHP 專案的首選資料庫介面。