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

PHP SQL 注入攻擊原理與防禦完整指南

     編輯:PHP 綜合知識

什麼是 SQL 注入攻擊?

SQL 注入(SQL Injection)是發生在應用程式資料庫層的安全漏洞。簡單來說,攻擊者在輸入字串中植入惡意 SQL 指令,若程式未妥善檢查輸入,資料庫伺服器會誤將這些指令視為正常 SQL 執行,導致資料洩露、竄改甚至刪除。

許多人誤以為 SQL 注入只針對 Microsoft SQL Server,實則只要資料庫支援批次處理 SQL 指令(如 MySQL、PostgreSQL、Oracle 等),皆可能成為攻擊目標。

高風險成因

應用程式若存在下列情況,極易暴露於 SQL 注入風險:

  • 使用字串串接方式組合 SQL 指令
  • 資料庫連線使用過高權限帳號(如 SQL Server 的 sa 帳號)
  • 資料庫開放不必要且權限過大的功能(如 xp_cmdshell、OLE Automation 預存程序)
  • 過度信任使用者輸入,未限制字元長度、未檢查潛在指令

攻擊運作原理

SQL 指令可透過分號(;)串接多條命令;字串參數以單引號(')包裹,但連續兩個單引號('')在資料庫中視為一個單引號字元;註解符號 -- 或 /* ... */ 可使後續文字被忽略。若組合 SQL 字串時未對單引號進行轉義處理,攻擊者即可竄改原本 SQL 語法。

PHP 中的兩大核心原因

  1. php.ini 中 magic_quotes_gpc 設為 Off:新版 PHP 預設已開啟,但舊版伺服器仍可能關閉。
  2. 開發者未對資料型別進行檢查與轉義:這是最關鍵的防線。無論 magic_quotes_gpc 開啟與否,若缺乏輸入驗證,注入風險依然存在。

技術實作示範

情境一:magic_quotes_gpc = Off 時的注入攻擊

當 magic_quotes_gpc = Off,提交的變數不會自動轉義單引號、雙引號、反斜線等特殊字元,攻擊者可輕易構造惡意輸入。

<?php
if (isset($_POST["f_login"])) {
    // 連接資料庫...
    $t_strUname = $_POST["f_uname"];
    $t_strPwd = $_POST["f_pwd"];
    $t_strSQL = "SELECT * FROM tbl_users WHERE username='$t_strUname' AND password = '$t_strPwd' LIMIT 0,1";
    if ($t_hRes = mysql_query($t_strSQL)) {
        // 成功查詢後的處理...
    }
}
?>

攻擊者在使用者名稱欄位輸入 admin' --,即可繞過密碼驗證登入。

情境二:magic_quotes_gpc = On 但未做型別檢查

即使開啟 magic_quotes_gpc,若開發者直接將數值型欄位(如 id)未經驗證帶入 SQL,攻擊者仍可透過數字型注入(如 id=1 OR 1=1)竊取資料。

防禦最佳實踐

  1. 使用預處理語句:採用 PDO 或 MySQLi 的參數化查詢,徹底分離程式碼與資料。
  2. 最小權限原則:資料庫連線帳號僅授予必要權限,避免使用 root 或 sa。
  3. 關閉不必要的資料庫功能:停用 xp_cmdshell 等高危預存程序。
  4. 嚴格驗證與過濾輸入:針對預期資料型別(整數、字串、日期等)進行強型別檢查,並使用白名單驗證。
  5. 錯誤訊息不外洩:生產環境關閉詳細 SQL 錯誤回報,避免洩露資料庫結構。
  6. 部署 Web 應用程式防火牆(WAF):作為纵深防禦的補充層。

程式碼修正範例(使用 PDO 預處理)

<?php
$pdo = new PDO("mysql:host=localhost;dbname=testdb", "user", "pass");
$stmt = $pdo->prepare("SELECT * FROM tbl_users WHERE username = :uname AND password = :pwd LIMIT 1");
$stmt->execute([
    ':uname' => $_POST['f_uname'],
    ':pwd'   => $_POST['f_pwd']
]);
$user = $stmt->fetch();
if ($user) {
    // 登入成功
}
?>

總結:SQL 注入防禦的核心在於「永遠不信任使用者輸入」,配合參數化查詢與最小權限原則,才能從根本上杜絕風險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved