首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 登錄防 SQL 注入:原理、攻擊示例與三層防禦體系

     編輯:PHP 相關知識

SQL 注入是 Web 開發中最經典、危害最大的漏洞。本文以登錄頁為例,演示拼接 SQL 的注入過程,並構建**參數化查詢(核心)、輸入驗證/過濾(輔助)、最小權限/監控(纵深)**三層防禦體系。

脆弱代碼:字串拼接 SQL

<?php
// ❌ 極度危險:直接拼接用戶輸入
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';

$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = $mysqli->query($sql);

if ($result && $result->num_rows > 0) {
    // 登錄成功
    $_SESSION['user_id'] = $result->fetch_assoc()['id'];
    header('Location: /dashboard');
} else {
    die('用戶名或密碼錯誤');
}
?>

經典注入 Payload 測試

輸入字段Payload生成 SQL效果
password' OR '1'='1... password = '' OR '1'='1'繞過密碼檢查,登錄首個用戶
usernameadmin' --... username = 'admin' --' AND password = ...註釋掉密碼檢查,以 admin 登錄
username' UNION SELECT 1,2,3,4 --... UNION SELECT ...聯表查詢竊取其他表數據
password'; DROP TABLE users; --...; DROP TABLE users; --刪除表(需多語句支援)

核心防禦:預處理語句

<?php
// ✅ 正確:參數化查詢,數據與代碼徹底分離
$stmt = $mysqli->prepare("SELECT id, password_hash, role FROM users WHERE username = ?");
$stmt->bind_param("s", $username); // "s"=string
$stmt->execute();
$result = $stmt->get_result();

if ($row = $result->fetch_assoc()) {
    // 密碼驗證:使用 password_verify,不在 SQL 中比對明文/雜湊
    if (password_verify($password, $row['password_hash'])) {
        $_SESSION['user_id'] = $row['id'];
        $_SESSION['role'] = $row['role'];
        header('Location: /dashboard');
        exit;
    }
}
die('用戶名或密碼錯誤');
?>

輔助防禦:輸入驗證與過濾

<?php
// 1. 白名單驗證:用戶名只允許字母數字下劃線 3-20 位
if (!preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
    die('用戶名格式錯誤');
}

// 2. 長度限制:防止超長字串導致緩衝區問題
if (strlen($password) > 72) { // bcrypt 最大 72 字節
    die('密碼過長');
}

// 3. 通用過濾函數(僅作為輔助,不能替代預處理)
function clean_input(string $input): string {
    $input = trim($input);
    $input = stripslashes($input); // 兼容 magic_quotes_gpc 遺留
    $input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8'); // 防 XSS,非防 SQL 注入
    return $input;
}

// 4. 針對 SQL 的轉義(僅在無法使用預處理的極罕見情況下)
$safe = $mysqli->real_escape_string($input); // 僅用於字串字面量,不可用於標識符/數字
?>

纵深防禦:最小權限與監控

  • 數據庫用戶最小權限:Web 應用賬號僅擁有 SELECT, INSERT, UPDATE, DELETE 權限,**無 DROP, ALTER, CREATE, GRANT** 權限。
  • 禁用多語句執行:mysqli::multi_query 僅在遷移腳本使用,業務代碼嚴禁。
  • WAF / RASP:部署 ModSecurity + OWASP CRS、OpenRASP、或雲廠商 WAF,攔截明顯注入特徵。
  • 審計日誌:開啟 MySQL 通用日誌/審計插件,記錄所有失敗查詢、異常模式。
  • 錯誤信息不外洩:生產環境 display_errors=Off,用戶看到通用「系統錯誤」,詳細日誌寫入文件供開發排查。

現代框架中的最佳實踐

框架/工具防注入機制
Laravel Eloquent查詢構建器自動參數化、whereRaw 需手動綁定
Symfony DoctrineDQL 參數化、setParameter
ThinkPHPDb::name('user')->where('name', $name)->find() 自動參數化
PDO$stmt = $pdo->prepare(...); $stmt->execute([$param])
Medoo鏈式調用自動參數化

總結:防 SQL 注入鐵律

  1. 永不拼接 SQL 字串—— 所有動態值必須用參數化查詢(? / :name)。
  2. 永不在 SQL 中處理密碼—— 存儲 password_hash(),驗證用 password_verify()。
  3. 輸入驗證是業務邏輯,非安全邊界—— 白名單正則、長度、類型檢查。
  4. 數據庫賬號最小權限—— 無 DROP/ALTER,限制單表/列權限(MySQL 8.0+ 支援列級權限)。
  5. 錯誤不回顯、日誌留存—— 生產環境關閉顯示錯誤,記錄完整堆疊供事後分析。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved