「永遠不要信任用戶輸入」是 Web 安全鐵律。PHP 內建多種過濾函數,各有適用場景與限制。本文系統整理 6 大核心函數:mysql_real_escape_string、addslashes、htmlentities、htmlspecialchars、strip_tags、intval,解析原理、適用場景、常見誤區與現代最佳實踐。
| 函數 | 核心用途 | 防護目標 | 現代建議 |
|---|---|---|---|
mysql_real_escape_string | SQL 字串轉義 | SQL 注入 | 棄用,改用 PDO 預處理 |
addslashes | 通用字串轉義 | SQL 注入 (不完整) | 不推薦,字元集敏感、易雙重轉義 |
htmlentities | 所有字元轉 HTML 實體 | XSS | 適用完整內容編碼,較重 |
htmlspecialchars | 特殊字元轉 HTML 實體 | XSS | 首選,效能好、覆蓋核心風險字元 |
strip_tags | 移除 HTML/JS/PHP 標籤 | XSS、標籤注入 | 允許特定標籤時使用,不可單獨防 XSS |
intval | 強制轉整數 | 型別混淆、SQL 注入 (數值欄位) | 數值參數驗證首選 |
<?php
$input = '';
$safe = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// 輸出: <script>alert("XSS")</script>
echo $safe; // 瀏覽器顯示原始文字,不執行腳本
?>
& → &、" → " (需 ENT_QUOTES)、' → ' (需 ENT_QUOTES)、< → <、> → >。$string、$flags = ENT_QUOTES | ENT_SUBSTITUTE、$encoding = 'UTF-8'。htmlspecialchars 重,輸出體積大。<?php $input = '正常段落
粗體'; echo strip_tags($input); // 正常段落alert(1)粗體 echo strip_tags($input, ''); // 保留
和 :
正常段落
alert(1)粗體 ?>
關鍵限制:
<img src=x onerror=alert(1)> → 標籤被保留 (若允許 img) 但 onerror 執行。strip_tags 只能作為輔助清理,**永遠搭配 htmlspecialchars 輸出編碼**。<?php // 取得 ID 參數 $id = isset($_GET['id']) ? intval($_GET['id']) : 0; // $id 永遠是整數,SQL: "SELECT * FROM posts WHERE id = $id" 安全 (無引號注入風險) // 同理適用:age, page, limit, offset, status 等數值欄位 $page = max(1, intval($_GET['page'] ?? 1)); $limit = min(100, max(1, intval($_GET['limit'] ?? 20))); ?>
floatval(),布林用 filter_var($v, FILTER_VALIDATE_BOOLEAN)。mysql_real_escape_string:
mysql_* 擴展,PDO/mysqli 皆有更好方案。addslashes:
magic_quotes_gpc (PHP 5.4 移除) 曾自動執行此函數,造成雙重轉義災難。filter_var, intval, 正則)。htmlspecialchars($str, ENT_QUOTES, 'UTF-8')json_encode($str) (自動處理引號、換行、Unicode)urlencode() / rawurlencode()pdo->quote()Content-Security-Policy 作為第二道防線。| 誤區 | 正確做法 |
|---|---|
| 儲存前 htmlspecialchars 入庫 | 儲存原始資料,輸出時編碼。入庫編碼會導致資料污染、重複編碼、搜尋失效。 |
| strip_tags 就能防 XSS | 無法防屬性事件、JS 協定、CSS 攻擊。必須搭配 htmlspecialchars。 |
| addslashes 防 SQL 注入 | 使用 PDO/mysqli 預處理。 |
| 過濾關鍵字 (select, union...) | 誤殺率高、繞過易。參數化查詢才是根本解。 |