PHP MySQL 安全性
資料庫安全是 Web 應用程式中最重要的環節之一。
SQL Injection 攻擊
SQL Injection 是最常見的資料庫攻擊方式:
<?php
// ❌ 危險:直接拼接使用者輸入
$username = $_POST['username'];
$sql = "SELECT * FROM users WHERE username = '$username'";
// 攻擊者輸入:' OR '1'='1
// 結果:SELECT * FROM users WHERE username = '' OR '1'='1'
// 會回傳所有使用者!
?>
預處理語句防護
永遠使用預處理語句(Prepared Statements):
<?php
// ✅ 安全:使用預處理語句
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$_POST['username']]);
$user = $stmt->fetch();
// 使用命名參數
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(['username' => $_POST['username']]);
?>
輸入驗證
<?php
function validateUserInput(array $data): array {
$errors = [];
// 必填檢查
if (empty($data['username'])) {
$errors[] = '使用者名稱為必填';
}
// 格式檢查
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Email 格式不正確';
}
// 長度檢查
if (strlen($data['username']) > 50) {
$errors[] = '使用者名稱過長';
}
// 型別檢查
if (!is_numeric($data['age'])) {
$errors[] = '年齡必須是數字';
}
return $errors;
}
?>
密碼安全
<?php
// ✅ 正確:使用 password_hash
$password = $_POST['password'];
$hash = password_hash($password, PASSWORD_DEFAULT);
// 儲存 $hash 到資料庫
$stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
$stmt->execute([$username, $hash]);
// 驗證密碼
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
echo "登入成功";
} else {
echo "帳號或密碼錯誤";
}
?>
絕對不要使用 md5() 或 sha1() 儲存密碼,這些已不安全。
最小權限原則
-- 建立專用資料庫使用者
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'strong_password';
-- 只給予必要的權限
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'webapp'@'localhost';
-- 不要給予 DROP、ALTER 等危險權限
-- GRANT ALL PRIVILEGES ... -- 避免使用
錯誤處理
<?php
// ❌ 危險:顯示詳細錯誤
try {
$pdo->query($sql);
} catch (PDOException $e) {
echo $e->getMessage(); // 可能洩漏資料庫結構
}
// ✅ 安全:記錄錯誤,顯示通用訊息
try {
$pdo->query($sql);
} catch (PDOException $e) {
error_log($e->getMessage()); // 記錄到日誌
echo "系統發生錯誤,請稍後再試"; // 顯示給使用者
}
?>
環境設定分離
<?php
// config.php(不要放在版本控制中)
return [
'db' => [
'host' => getenv('DB_HOST') ?: 'localhost',
'name' => getenv('DB_NAME') ?: 'myapp',
'user' => getenv('DB_USER') ?: 'root',
'pass' => getenv('DB_PASS') ?: '',
],
];
// 使用
$config = require 'config.php';
$pdo = new PDO(
"mysql:host={$config['db']['host']};dbname={$config['db']['name']}",
$config['db']['user'],
$config['db']['pass']
);
?>
XSS 防護
輸出資料時要跳脫:
<?php
// ❌ 危險
echo $user['name'];
// ✅ 安全
echo htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8');
// 建立輔助函數
function e(string $string): string {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
echo e($user['name']);
?>
安全檢查清單
- ☑️ 所有 SQL 查詢都使用預處理語句
- ☑️ 密碼使用
password_hash()儲存 - ☑️ 驗證和清理所有使用者輸入
- ☑️ 輸出時使用
htmlspecialchars() - ☑️ 資料庫使用者權限最小化
- ☑️ 敏感設定使用環境變數
- ☑️ 錯誤訊息不洩漏系統資訊
- ☑️ 使用 HTTPS 傳輸敏感資料