跨站腳本攻擊 (XSS) 是 Web 最常見漏洞。本文提供一個健壯的 remove_xss() 函數,涵蓋:非可列印字元清理、實體編碼還原、事件處理器/危隆標籤移除、十六進位/十進位實體攻擊向量阻斷、遞迴清理直到無變化。適合作為輸入端通用清洗 (輸出端仍建議配合 htmlspecialchars 依上下文編碼)。
function remove_xss($val) {
// 1. 移除控制字元 (保留 \t \n \r)
$val = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $val);
// 2. 實體還原:將 HH; DDD; 還原為原字元,防止繞過
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()~`";:?/={}[]-_|\'\\';
for ($i = 0; $i < strlen($chars); $i++) {
$c = $chars[$i];
$hex = dechex(ord($c));
$dec = ord($c);
$val = preg_replace("/*$hex;?/i", $c, $val);
$val = preg_replace("/*$dec;?/", $c, $val);
}
// 3. 危險關鍵字清單 (標籤 + 事件屬性)
$tags = ['javascript','vbscript','expression','applet','meta','xml','blink','link','style','script','embed','object','iframe','frame','frameset','ilayer','layer','bgsound','title','base'];
$events = ['onabort','onactivate','onafterprint','onafterupdate','onbeforeactivate','onbeforecopy','onbeforecut','onbeforedeactivate','onbeforeeditfocus','onbeforepaste','onbeforeprint','onbeforeunload','onbeforeupdate','onblur','onbounce','oncellchange','onchange','onclick','oncontextmenu','oncontrolselect','oncopy','oncut','ondataavailable','ondatasetchanged','ondatasetcomplete','ondblclick','ondeactivate','ondrag','ondragend','ondragenter','ondragleave','ondragover','ondragstart','ondrop','onerror','onerrorupdate','onfilterchange','onfinish','onfocus','onfocusin','onfocusout','onhelp','onkeydown','onkeypress','onkeyup','onlayoutcomplete','onload','onlosecapture','onmousedown','onmouseenter','onmouseleave','onmousemove','onmouseout','onmouseover','onmouseup','onmousewheel','onmove','onmoveend','onmovestart','onpaste','onpropertychange','onreadystatechange','onreset','onresize','onresizeend','onresizestart','onrowenter','onrowexit','onrowsdelete','onrowsinserted','onscroll','onselect','onselectionchange','onselectstart','onstart','onstop','onsubmit','onunload'];
$patterns = array_merge($tags, $events);
// 4. 遞迴替換:在關鍵字字元間插入 破壞結構
$changed = true;
while ($changed) {
$changed = false;
$old = $val;
foreach ($patterns as $kw) {
$pattern = '';
for ($j = 0; $j < strlen($kw); $j++) {
if ($j > 0) {
// 允許字元間夾雜實體編碼 (\t \n \r 等)
$pattern .= '(?:*[9ab];|*[9|10|13];)*';
}
$pattern .= preg_quote($kw[$j], '/');
}
$pattern = "/$pattern/i";
$replacement = substr($kw, 0, 2) . '' . substr($kw, 2);
$val = preg_replace($pattern, $replacement, $val);
}
if ($val !== $old) $changed = true;
}
return $val;
}
$evil = ''; echo remove_xss($evil); // 輸出:
ror=alert(1)> $evil2 = ''; echo remove_xss($evil2); // 輸出:
ript>ev ript>al(St ring.fr omCh arCode(97,108,101,114,116,40,49,41))
| 層級 | 措施 | 說明 |
|---|---|---|
| 1. 輸入過濾 | remove_xss() / HTMLPurifier | 儲存前清洗,降低資料庫毒化風險 |
| 2. 輸出編碼 | htmlspecialchars($str, ENT_QUOTES, 'UTF-8') | **最關鍵**:依上下文 (HTML/JS/CSS/URL) 編碼 |
| 3. CSP 標頭 | Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn; | 瀏覽器級防線,禁止內聯腳本、限制來源 |
| 4. HttpOnly Cookie | setcookie(..., true, true) | 防止 JS 竊取 Session ID |
| 5. 框架自動逃逸 | Laravel Blade {{ }}, Twig {{ }}, Vue/React 自動轉義 | 使用現代框架預設安全 |
本函數適合輕量級專案或作為第二道防線,**切勿單獨依賴** 替代輸出編碼與 CSP。