magic_quotes_gpc = Off(舊版 PHP 預設關閉,新版已移除)即使開啟 `magic_quotes_gpc`,若未驗證數值型欄位,仍可被注入。
$sql = "SELECT * FROM users WHERE username='$_POST[user]' AND pwd='$_POST[pwd]'";
輸入:`user = admin' OR 1=1 #` → SQL 變為:
SELECT * FROM users WHERE username='admin' OR 1=1 #' AND pwd='...'
註解符 `#` 截斷後續查詢,繞過驗證。若知表結構可用 `UNION SELECT` 竊取資料。
$sql = "SELECT * FROM users WHERE uid=$_POST[uid] AND pwd='$_POST[pwd]'";
輸入:`uid = 1001 OR 1=1 #` → SQL:
SELECT * FROM users WHERE uid=1001 OR 1=1 # AND pwd='...'
數值型欄位**不需引號**,`magic_quotes` 不會轉義,直接注入成功。MySQL 5.x+ 已嚴格型別,但舊版或未嚴格模式下極危險。
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ? AND pwd = ?');
$stmt->execute([$_POST['user'], $_POST['pwd']]);
$user = $stmt->fetch();
**優點**:資料與指令分離,徹底杜絕注入;效能佳(重複執行只編譯一次)。
| 措施 | 說明 |
|---|---|
| 型別強制轉換 | `$id = (int)$_GET['id'];` 確保數值型 |
| 白名單驗證 | `in_array($sort, ['asc','desc'])` 限制可選值 |
| 最小權限資料庫帳號 | 僅授予必要 CRUD 權限,禁止 DROP/ALTER |
| 關閉錯誤顯示 | `display_errors=Off`,記錄至 log,避免洩露結構 |
| WAF / 參數過濾 | 應用層過濾可疑關鍵字(非首選,易誤殺/繞過) |
$sql = "SELECT * FROM t WHERE c = '" . addslashes($_GET['c']) . "'";
// 危險:多位元組編碼繞過、邊界情況、維護成本高
function getParam(string $name, $default = '', string $type = 'string')
{
$val = $_GET[$name] ?? $_POST[$name] ?? $default;
$val = trim($val);
if ($type === 'int') {
return is_numeric($val) ? (int)$val : (int)$default;
}
// 字串:HTML 編碼輸出端處理,資料庫端用參數化查詢
return $val;
}