SQL 注入發生於使用者輸入未經過濾直接拼接進 SQL 語句。本文分析 Union 查詢注入、登入繞過、錯誤回顯等經典手法,並給出完整防禦方案。
<?php
// 易受攻擊代碼
$id = $_GET['id']; // 無過濾
$sql = "SELECT * FROM articles WHERE id = $id"; // 直接拼接
$result = mysql_query($sql);
?>
攻擊 Payload:
// 原始: SELECT * FROM articles WHERE id = 1
// 注入: id=1 AND 1=2 UNION SELECT 1,username,password,4,5 FROM admin--
// 結果: SELECT * FROM articles WHERE id = 1 AND 1=2 UNION SELECT 1,username,password,4,5 FROM admin--
條件:
ORDER BY 1,2,3... 或 UNION SELECT NULL,NULL... 試探)。information_schema 查詢)。<?php
// 易受攻擊代碼
$user = $_POST['username'];
$pass = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$user' AND password = '$pass'";
$result = mysql_query($sql);
if (mysql_num_rows($result) > 0) { /* 登入成功 */ }
?>
經典 Payload:
| 輸入欄位 | 值 | 產生 SQL 片段 |
|---|---|---|
| username | ' OR '1'='1 | username = '' OR '1'='1 |
| password | ' OR '1'='1 | password = '' OR '1'='1 |
| username | admin'-- | username = 'admin'-- (註解掉密碼檢查) |
| username | ' OR 1=1# | username = '' OR 1=1# (MySQL 註解) |
結果:WHERE 條件恆真,返回第一筆記錄 (通常是管理員),繞過驗證。
<?php
// 開啟錯誤顯示 (生產環境嚴禁)
ini_set('display_errors', 1);
$id = $_GET['id'];
$sql = "SELECT * FROM products WHERE id = $id";
mysql_query($sql) or die(mysql_error()); // 直接輸出 SQL 錯誤
?>
攻擊者輸入 id=1' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(VERSION(),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)-- 觸發重複鍵錯誤,將資料庫版本、表結構等資訊透過錯誤訊息洩露。
<?php
// PDO
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$user, $pass]);
// MySQLi
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param('ss', $user, $pass);
$stmt->execute();
$id = (int)$_GET['id']; // 強制整數
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
SELECT/INSERT/UPDATE/DELETE,無 DROP/ALTER/GRANT。ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php_errors.log');
SQL 注入是 OWASP Top 10 長年榜首,根治之道在於「資料與代碼分離」——參數化查詢。任何字串拼接 SQL 的代碼都是定時炸彈,請立即審查並修復。