資料進出資料庫是 Web 應用核心環節,正確處理轉義、編碼與輸出過濾是安全基石。本文系統整理入庫、出庫、顯示三階段處理規範。
現代最佳實踐:使用 PDO 或 mysqli 預處理語句,徹底杜絕 SQL 注入:
<?php
// PDO 範例
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('INSERT INTO articles (title, content) VALUES (?, ?)');
$stmt->execute([$title, $content]); // 自動處理轉義
// mysqli 範例
$mysqli = new mysqli('localhost', $user, $pass, 'test');
$stmt = $mysqli->prepare('INSERT INTO articles (title, content) VALUES (?, ?)');
$stmt->bind_param('ss', $title, $content);
$stmt->execute();
// 僅供維護舊專案參考
$str = addslashes($str); // 轉義單引號、雙引號、反斜線、NULL
$sql = "INSERT INTO `tab` (`content`) VALUES ('$str')";
// 對應 addslashes 的反向操作
$str = stripslashes($str);
⚠️ 若使用參數化查詢,資料庫存儲的是原始資料,**不需要** 此步驟。
// 通用安全輸出:轉義 HTML 特殊字元 + 換行轉 br
echo htmlspecialchars(nl2br($str), ENT_QUOTES, 'UTF-8');
<?php
// 1. 標題、名稱等單行欄位:去首尾空格 + 防注入 + 安全輸出
function transStringTrim($str) {
$str = trim($str);
$str = stripslashes($str); // 若舊資料有 addslashes
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
// 2. 文章內容 (textarea) 入庫:僅處理引號 (配合參數化查詢)
function transString($str) {
return $str; // 參數化查詢下無需額外處理
}
// 3. 表單回填顯示 (input/textarea)
function transForm($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
// 4. 網頁顯示:過濾 HTML + 換行轉 br
function transWeb($string) {
$string = htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
$string = nl2br($string);
return $string;
}
// 5. 允許部分 HTML (如編輯器內容) - 需配合 HTMLPurifier
function transWebAllowHtml($string) {
// 這裡應調用 HTMLPurifier::purify()
return nl2br($string);
}
// 6. 用戶暱稱等需去空格 + 防 XSS
function transTrim($string) {
return htmlspecialchars(trim($string), ENT_QUOTES, 'UTF-8');
}
// 7. JavaScript 字串/JSON 輸出
function transJs($string) {
return json_encode($string); // 自動處理引號、換行、Unicode
}
// 8. URL 參數輸出
function transUrl($string) {
return urlencode($string);
}
// 9. HTML 屬性值輸出
function transAttr($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
| 層級 | 設定 |
|---|---|
| 資料庫 | CHARSET=utf8mb4, COLLATE=utf8mb4_unicode_ci |
| 連線 | PDO: charset=utf8mb4 / mysqli: set_charset('utf8mb4') |
| PHP 檔案 | 儲存為 UTF-8 無 BOM |
| HTTP 回應 | header('Content-Type: text/html; charset=UTF-8') |
| HTML 頁面 | <meta charset="UTF-8"> |
| 錯誤做法 | 問題 | 正確做法 |
|---|---|---|
| 直接拼接 SQL 字串 | SQL 注入風險極高 | 參數化查詢 |
| 用 addslashes 防注入 | 多位元組編碼可繞過 | 參數化查詢 |
| 輸出未用 htmlspecialchars | XSS 漏洞 | 所有輸出必經轉義 |
| 用 str_replace 過濾腳本 | 繞過手法極多 | HTMLPurifier 白名單過濾 |
| 資料庫用 utf8 (非 utf8mb4) | 不支援 Emoji/罕見字 | 統一 utf8mb4 |
htmlspecialchars()json_encode()urlencode()