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

PHP addslashes 與 stripslashes 深度解析:magic_quotes 歷史遺產與現代最佳實踐

     編輯:PHP 相關知識

前言

addslashes()、stripslashes() 與已移除的 magic_quotes_gpc 是 PHP 早期為防止 SQL 注入而引入的機制,但因設計缺陷導致無數雙重轉義、資料損壞、安全誤區。本文深度解析其運作原理、歷史坑點、現代參數化查詢取代方案,以及維護舊專案時的正確處理策略。

函數行為定義

函數操作轉換對照
addslashes($str)在特定字元前加反斜線'→\', "→\", \→\\, NULL→\0
stripslashes($str)移除反斜線轉義\'→', \\→\, \"→", \0→NULL

magic_quotes_gpc 歷史災難

PHP 4/5 預設開啟 magic_quotes_gpc = On
→ 自動對 $_GET, $_POST, $_COOKIE 執行 addslashes()
→ 開發者以為「已安全」,直接串接 SQL
→ 問題:
  1. 雙重轉義:開發者又手動 addslashes() → \\' 寫入 DB
  2. 輸出時需 stripslashes(),否則頁面顯示 O\'Reilly
  3. 不同伺服器設定不同 (On/Off),代碼移植必壞
  4. 無法防禦所有注入 (如數值型注入、編碼繞過)
  5. 影響 JSON、XML、檔案上傳等非 SQL 場景
PHP 5.4 移除 magic_quotes_gpc,PHP 7 移除相關函數

經典錯誤模式與修正

錯誤 1:盲目雙重轉義

<?php
// magic_quotes_gpc = On 時
$name = $_POST['name'];           // 已自動 addslashes: O\'Reilly
$name = addslashes($name);        // 再次轉義: O\\\'Reilly
$sql = "INSERT INTO users (name) VALUES ('$name')"; // 寫入 DB: O\\\'Reilly
// 讀出顯示: O\\'Reilly (需 stripslashes 兩次才還原)
?>

修正:統一處理流程

<?php
// 1. 入口檔案 (bootstrap.php) 統一清理 magic_quotes 影響
if (function_exists('get_magic_quotes_gpc') && 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);
    $_REQUEST = stripslashes_deep($_REQUEST);
}

// 2. 之後代碼完全不使用 addslashes/stripslashes
// 3. 所有 SQL 使用參數化查詢 (PDO / mysqli prepare)
$stmt = $pdo->prepare("INSERT INTO users (name) VALUES (?)");
$stmt->execute([$_POST['name']]); // 完全安全,無需轉義
?>

現代最佳實踐:參數化查詢 (Prepared Statements)

PDO 範例

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

// 插入
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->execute([$name, $email]);

// 查詢
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? AND status = ?");
$stmt->execute([$email, 1]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

// 批次插入
$pdo->beginTransaction();
$stmt = $pdo->prepare("INSERT INTO logs (msg) VALUES (?)");
foreach ($messages as $msg) {
    $stmt->execute([$msg]);
}
$pdo->commit();
?>

mysqli 範例

<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'db');
$stmt = $mysqli->prepare("INSERT INTO users (name) VALUES (?)");
$stmt->bind_param('s', $name);
$stmt->execute();
?>

何時仍需 addslashes/stripslashes?

幾乎不需要。極少數情境:

  1. 輸出 JavaScript 字串常量:echo "var msg = '" . addslashes($str) . "';" (更好用 json_encode())。
  2. 舊系統無法改用參數化查詢:暫時用 mysqli_real_escape_string() (針對連線字元集) 而非 addslashes()。
  3. 處理非 SQL 的轉義需求:如產生 Shell 指令參數 (應用 escapeshellarg())、正則模式 (應用 preg_quote())。

維護舊專案遷移清單

  1. 確認 php.ini magic_quotes_gpc = Off (PHP 5.4+ 已移除)。
  2. 2. 在入口點加入 stripslashes_deep 清理超全域變數 (如上)。 3. 全域搜尋 addslashes、stripslashes、mysql_escape_string、mysql_real_escape_string。 4. 逐個替換為 PDO::prepare / mysqli::prepare + bind_param / execute。 5. 移除所有 stripslashes() 輸出端呼叫 (資料庫已存原始資料)。 6. 執行完整測試套件,特別檢查含單引號、反斜線、多位元組字元的資料。 7. 部署後監控 SQL 錯誤日誌、資料完整性。

總結

addslashes()/stripslashes() 是 已棄用的歷史產物。現代 PHP 開發完全不需要這兩個函數。參數化查詢 (Prepared Statements) 從根本上分離代碼與資料,徹底解決 SQL 注入,且無編碼、雙重轉義、效能等副作用。維護舊專案時,優先在入口點清理 magic_quotes 殘留影響,再系統性遷移至參數化查詢,即可獲得安全、乾淨、可維護的代碼庫。

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