PHP ≤ 5.3 內建 magic_quotes_gpc = On 預設開啟,自動對 $_GET/$_POST/$_COOKIE 進行 addslashes() (單引號、雙引號、反斜線、NULL 字元前加反斜線)。PHP 5.4 **已移除**此功能,現代框架 (Laravel、Symfony、Yii) 均假設關閉並自行處理逃逸。
| 場景 | magic_quotes_gpc | 寫入 DB 前 | DB 實際儲存 | 讀出後輸出 |
|---|---|---|---|---|
| 未處理 | Off | - | 報錯或截斷 (單引號未逃逸) | - |
| addslashes | Off | addslashes($str) | 正常 (單引號變 \') | 直接 echo 正常 |
| addslashes | On | addslashes($str) (雙重逃逸) | 變 \\' (多一層反斜線) | 需 stripslashes() 還原 |
| 不處理 | On | - | 正常 (系統已逃逸) | 直接 echo 正常 |
<?php
// PDO 範例 (推薦)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false, // 真實預處理
]);
$stmt = $pdo->prepare('INSERT INTO articles (title, content) VALUES (?, ?)');
$stmt->execute([$title, $content]); // 完全不需手動逃逸,驅動層處理
// 查詢
$stmt = $pdo->prepare('SELECT * FROM articles WHERE id = ?');
$stmt->execute([$id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo $row['title']; // 直接輸出,無需 stripslashes
// mysqli 手動逃逸 (僅限字串字面值,識別碼需反引號)
$title = $mysqli->real_escape_string($title);
$sql = "INSERT INTO t (title) VALUES ('$title')"; // 注意單引號包裹
// 讀出後直接輸出,不需 stripslashes (real_escape_string 不會在資料庫存反斜線)
資料庫儲存原始文字,輸出時才逃逸 (Context-Aware Escaping):
<?= htmlspecialchars($row['title'], ENT_QUOTES | ENT_HTML5, 'UTF-8') ?>
// HTML 內容
<?= htmlspecialchars($row['content'], ENT_QUOTES | ENT_HTML5, 'UTF-8') ?>
// JS 變量
<script> var title = <?= json_encode($row['title'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;</script>
// URL 參數
<a href="?title=<?= urlencode($row['title']) ?>">
遵循 Filter Input, Escape Output 原則,資料流向清晰、安全、可維護。