首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

安全顯示用戶輸入內容:從 HTML 特殊字元轉義到自訂標籤解析的完整防禦策略

     編輯:PHP 相關知識

核心原則:永遠不信任用戶輸入

直接輸出用戶提交的內容會導致 XSS (跨站腳本攻擊)、版面破壞、資訊洩露。防禦核心是輸出端編碼與白名單過濾。

三層防禦策略

第一層:完全轉義 —— 最安全、最簡單

<?php
function safe_text_output($input) {
    return htmlspecialchars($input, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

// 使用
echo "
" . safe_text_output($_POST['comment']) . "
"; // 輸出範例 // 輸入: 粗體 // 輸出: <script>alert(1)</script><b>粗體</b> // 瀏覽器顯示為純文字,不執行、不渲染標籤

適用:純文字欄位、搜尋關鍵字、JSON API 回傳、日誌記錄。

第二層:自訂標籤 (BBCode/Markdown 風格) —— 受控豐富格式

允許用戶使用安全子集:[b]粗體[/b]、[i]斜體[/i]、[url=...]連結[/url]。

實作步驟

  1. 先完全轉義,消除所有原生 HTML 風險。
  2. 白名單替換:僅將特定自訂標籤轉為對應 HTML。
<?php
function format_custom_markup($text) {
    // 1. 完全轉義
    $text = htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    
    // 2. 簡單標籤替換 (str_replace 足夠)
    $text = str_replace('[b]', '', $text);
    $text = str_replace('[/b]', '', $text);
    $text = str_replace('[i]', '', $text);
    $text = str_replace('[/i]', '', $text);
    $text = str_replace('[u]', '', $text);
    $text = str_replace('[/u]', '', $text);
    
    // 3. 複雜標籤用正則 (連結、圖片、代碼塊)
    // [url=https://example.com]連結文字[/url]
    $text = preg_replace(
        '/\[url=([^\]]+)\](.*?)\[\/url\]/is',
        '$2',
        $text
    );
    
    // [img]https://example.com/image.png[/img]
    $text = preg_replace(
        '/\[img\](https?:\/\/[^\s\]]+)\[\/img\]/i',
        '用戶圖片',
        $text
    );
    
    // [code]程式碼[/code]
    $text = preg_replace(
        '/\[code\](.*?)\[\/code\]/is',
        '
$1
', $text ); // 4. 換行轉
(最後處理) $text = nl2br($text); return $text; }

進階:巢狀標籤與平衡檢查

簡單替換無法處理 [b][i]文字[/b][/i] 錯誤巢狀。建議:

  • 使用 狀態機解析器 或 HTMLPurifier 庫。
  • 或限制不支援巢狀,檢測到不平衡即拒絕/修正。

第三層:HTMLPurifier —— 企業級白名單 HTML 過濾

若必須允許用戶輸入真實 HTML (如富文本編輯器),必須用 HTMLPurifier,手寫正則極難完備。

<?php
require_once 'HTMLPurifier.auto.php';

$config = HTMLPurifier_Config::createDefault();
// 允許的標籤與屬性
$config->set('HTML.Allowed', 'p,b,strong,i,em,u,strike,a[href|title|target|rel],img[src|alt|width|height],br,ul,ol,li,blockquote,pre,code,h1,h2,h3,h4,h5,h6');
// 允許的 CSS 樣式 (可選)
$config->set('CSS.AllowedProperties', 'font-weight,font-style,text-decoration,color,background-color,text-align');
// 目標輸出編碼
$config->set('Core.Encoding', 'UTF-8');

$purifier = new HTMLPurifier($config);

$clean_html = $purifier->purify($dirty_html_from_user);
// $clean_html 保證安全、標準、無 XSS

常見錯誤與避坑

錯誤做法 風險 正確做法
只過濾 <script>繞過極易 (事件屬性、CSS、編碼混淆)完整轉義或白名單
strip_tags() 允許部分標籤屬性未過濾 (onclick、style、onload)HTMLPurifier
儲存前過濾,輸出不處理二次注入、語境不同 (HTML/JS/URL/CSS)輸出端編碼 (儲存原文)
JS 端過濾繞過、禁用 JS 失效伺服器端處理
正則匹配 <.*?> 移除標籤貪婪/非貪婪失效、屬性值含 > 截斷解析器/HTMLPurifier

語境感知輸出編碼 (Context-Aware Output Encoding)

同一資料在不同語境需不同編碼:

語境 編碼函式 範例
HTML 內文/屬性值htmlspecialchars($str, ENT_QUOTES, 'UTF-8')<div>
HTML 屬性 (單引號)htmlspecialchars($str, ENT_QUOTES, 'UTF-8')<input value=''>
JavaScript 字串json_encode($str) (含引號)<script>var x = ;</script>
URL 參數值urlencode($str)<a href="?q=">
CSS 值專用編碼 (極少用)<div style="color: ;">

完整防禦清單

  1. 輸入端:長度限制、字符集驗證 (UTF-8)、業務規則驗證 (Email、電話格式) —— 不做 HTML 過濾。
  2. 儲存端:儲存原始用戶輸入 (參數化查詢防 SQL 注入)。
  3. 輸出端:根據語境選擇編碼/過濾策略 (純文字→htmlspecialchars、受控格式→自訂標籤/Markdown、富文本→HTMLPurifier)。
  4. HTTP 標頭:Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; 作為最後防線。
  5. Cookie:HttpOnly、Secure、SameSite=Strict/Lax。

總結

安全顯示用戶內容的鐵律:儲存原文、輸出編碼、語境感知、白名單過濾。簡單場景用 htmlspecialchars(),受控格式用自訂標籤/Markdown 解析器,複雜富文本務必用 HTMLPurifier。切記:過濾在輸出端,而非輸入端。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved