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

PHP get_magic_quotes_gpc() 歷史遺留問題與現代防 SQL 注入最佳實踐

     編輯:PHP 相關知識

get_magic_quotes_gpc() 是 PHP 早期 (≤ 5.3) 的**魔術引號**功能檢測函數。該功能**已在 PHP 5.4 移除**,**PHP 7+ 完全不存在**。現代專案**絕對不應依賴**此功能,而應使用**預處理語句**防禦 SQL 注入。本文說明歷史背景、遷移策略與現代最佳實踐。

歷史背景:Magic Quotes 是什麼?

  • PHP ≤ 5.3 時,php.ini 可設定 magic_quotes_gpc = On。
  • 開啟後,$_GET, $_POST, $_COOKIE 中的 ', ", \, NULL 字元會**自動加反斜線轉義**。
  • get_magic_quotes_gpc() 回傳 1 (On) 或 0 (Off)。
  • 目的:減少新手直接拼接 SQL 導致注入。
  • **嚴重缺陷**:
    • 只針對 GPC,不覆蓋 $_FILES, $_SERVER, 資料庫讀出資料等。
    • 導致資料「雙重轉義」 (開啟時又手動 addslashes())。
    • 給開發者錯誤安全感,忽略正確防禦。
    • 不同伺服器設定不同,程式碼不可移植。
  • **PHP 5.4 移除**,**PHP 7.0 函數不存在** (呼叫報 Fatal Error)。

遷移策略:從 Magic Quotes 到現代防禦

步驟 1:關閉 Magic Quotes (PHP 5.3 環境)

; php.ini
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
<?php
// 運行時關閉 (若支援)
if (function_exists('set_magic_quotes_runtime')) {
    set_magic_quotes_runtime(0);
}

// 若已開啟,需在入口處清理
if (get_magic_quotes_gpc()) {
    function stripslashes_deep($value) {
        return is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
    }
    $_GET = stripslashes_deep($_GET);
    $_POST = stripslashes_deep($_POST);
    $_COOKIE = stripslashes_deep($_COOKIE);
}
?>

步驟 2:使用預處理語句 (PDO / MySQLi) — 唯一正確防禦

<?php
// PDO (推薦)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false, // 真正預處理
]);

$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status = ?');
$stmt->execute([$email, 1]);
$user = $stmt->fetch();

// 參數自動轉義、型別處理,完全防注入
?>
<?php
// MySQLi
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$mysqli->set_charset('utf8mb4');

$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ?');
$stmt->bind_param('s', $email); // 's'=string, 'i'=int, 'd'=double, 'b'=blob
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
?>

步驟 3:輸出時防 XSS (htmlspecialchars)

<?php
function h(string $str): string {
    return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
echo '
' . h($userInput) . '
'; ?>

常見錯誤代碼與修正 (舊專案遺留)

舊代碼 (危險)問題現代修正
mysql_query("SELECT * FROM users WHERE name='$name'");直接拼接,注入漏洞PDO/MySQLi 預處理
$name = addslashes($name); mysql_query(...)addslashes 不完整 (不處理 %, _ 等)、編碼問題預處理
if (!get_magic_quotes_gpc()) $name = addslashes($name);依賴已移除函數、雙重轉義風險移除判斷,用預處理
mysql_real_escape_string($name)需連線資源、編碼依賴連線字符集、易忘預處理 (自動處理)

現代專案檢查清單

  • ☑️ **PHP ≥ 7.4** (建議 8.1+)
  • ☑️ **php.ini 確認 magic_quotes_gpc = Off** (預設即關閉)
  • ☑️ **程式碼無 get_magic_quotes_gpc()、set_magic_quotes_runtime()、addslashes() 用於 SQL**
  • ☑️ **所有 SQL 使用 PDO/MySQLi 預處理**
  • ☑️ **資料庫連線字符集 utf8mb4**
  • ☑️ **輸出用 htmlspecialchars() 防 XSS**
  • ☑️ **密碼用 password_hash() 儲存**

補充:何時仍需手動轉義?

極少數場景:

  • 構建 **LIKE 模式** 字串:$like = '%' . $pdo->quote($keyword) . '%'; (注意 quote() 包含引號)
  • 動態建構 **識別欄位/表名** (無法參數化):**嚴格白名單驗證**。
  • 舊系統無法改用預處理的緊急修補:用 $pdo->quote($value) (PDO) 或 $mysqli->real_escape_string($value) (MySQLi),但**必須設定正確連線字符集**。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved