嘗試列舉所有「危險字符」(單引號、分號、<script>、SQL 關鍵字等)屬於黑名單策略,存在三大缺陷:
兩者缺一不可,且不可混淆。
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 120]]);
$url = filter_var($input, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED);
$ip = filter_var($input, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
// 手機號(台灣範例)
if (!preg_match('/^09\d{8}$/', $phone)) {
throw new InvalidArgumentException('手機號格式錯誤');
}
// 中英文姓名,2-20 字元
if (!preg_match('/^[\p{Han}\p{L}\s\-\.]{2,20}$/u', $name)) {
throw new InvalidArgumentException('姓名格式錯誤');
}
$allowedStatus = ['pending', 'paid', 'shipped', 'cancelled'];
$status = $_POST['status'] ?? '';
if (!in_array($status, $allowedStatus, true)) {
throw new InvalidArgumentException('無效狀態值');
}
| 輸出上下文 | PHP 函數/方法 | 範例 |
|---|---|---|
| HTML 內容 | htmlspecialchars() | echo htmlspecialchars($userInput, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); |
| HTML 屬性 | htmlspecialchars() | <input value="<?= htmlspecialchars($val, ENT_QUOTES, 'UTF-8') ?>"> |
| JavaScript 字串 | json_encode() | <script>var data = <?= json_encode($val, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;</script> |
| URL 參數 | rawurlencode() | $url = 'search.php?q=' . rawurlencode($keyword); |
| SQL 查詢 | 預處理語句 | $stmt->execute([$param]);(而非字串拼接) |
| Shell 指令 | escapeshellarg() | exec('convert ' . escapeshellarg($input) . ' output.png'); |
ValidationException,由全域處理器回傳結構化錯誤訊息。放棄「過濾危險字符」的黑名單思維,轉而採用白名單驗證輸入格式 + 上下文感知輸出編碼 + 預處理語句處理 SQL 的組合拳,才能從根本上解決 XSS、SQLi、指令注入等注入類漏洞。