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

PHP 過濾表單提交 HTML 標籤:strip_tags、HTMLPurifier、XSS 防護完整指南

     編輯:PHP 相關知識

用戶提交的評論、留言、富文本若直接輸出會導致 XSS 攻擊、樣式破壞、惡意跳轉。本文從基礎 strip_tags 到企業級 HTMLPurifier,系統梳理 PHP 端 HTML 過濾方案,並給出不同業務場景的選型建議。

方案對比總覽

方案原理安全性性能保留樣式適用場景
strip_tags正則移除標籤⭐ 低(屬性殘留、編碼繞過)⚡ 極快❌ 全部移除純文本輸入(用戶名、標籤)
htmlspecialchars實體轉義⭐⭐⭐⭐⭐ 高⚡ 極快❌ 顯示源碼直接輸出到 HTML 文本節點
自定義允許列表正則/解析器保留特定標籤⭐⭐ 中(難處理嵌套、事件屬性)⚡⚡ 中✅ 部分簡單富文本(公告、備註)
HTMLPurifier詞法分析+白名單+安全過濾⭐⭐⭐⭐⭐ 極高🐢 慢(可緩存)✅ 完全可控富文本編輯器、CMS、論壇
前端過濾DOMPurify 等⚠️ 輔助,不可信⚡ 快✅即時預覽、減輕後端壓力

1. 基礎方案:strip_tags + htmlspecialchars

<?php
// 完全移除標籤,僅保留純文本
function plain_text(string $input): string {
    return strip_tags($input);
}

// 允許極少數安全標籤(b, i, u, a)
function simple_html(string $input): string {
    return strip_tags($input, '<b><i><u><a>');
}

// 輸出到 HTML 文本節點:必須轉義
function escape_html(string $input): string {
    return htmlspecialchars($input, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

// 組合:先過濾標籤,再轉義輸出
$userInput = '<script>alert(1)</script><b>粗體</b>';
echo escape_html(simple_html($userInput));
// 輸出:&lt;script&gt;alert(1)&lt;/script&gt;<b>粗體</b>
// 瀏覽器顯示:粗體(標籤被轉義為文本)
?>

⚠️ strip_tags 致命缺陷

  1. 屬性保留:<img src=x onerror=alert(1)> → strip_tags 保留 onerror 事件屬性
  2. 編碼繞過:<svg/onload=alert(1)>、Unicode 零寬字符干擾正則
  3. 嵌套標籤截斷:<b><script>...</b> 可能導致結構破壞
  4. 不處理 CSS:<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:red"> 可劫持頁面

2. 企業級方案:HTMLPurifier

<?php
// composer require ezyang/htmlpurifier
require_once 'vendor/autoload.php';

$config = HTMLPurifier_Config::createDefault();

// 核心配置
$config->set('HTML.Doctype', 'HTML 4.01 Transitional');
$config->set('HTML.Allowed', 'p,b,i,u,em,strong,a[href|title],ul,ol,li,br,img[src|alt|width|height],span[style],div[style]');
$config->set('CSS.AllowedProperties', 'font-weight,font-style,text-decoration,color,background-color,text-align,margin,padding');
$config->set('AutoFormat.RemoveEmpty', true);
$config->set('AutoFormat.AutoParagraph', true);

// 緩存配置(生產環境必開)
$config->set('Cache.SerializerPath', __DIR__ . '/storage/cache/htmlpurifier');

// URI 過濾:僅允許 http/https,禁止 javascript:、data:
$config->set('URI.AllowedSchemes', ['http' => true, 'https' => true, 'mailto' => true]);

$purifier = new HTMLPurifier($config);

// 使用
$dirty = '<script>alert(1)</script><p style="color:red">合法</p><a href="javascript:evil()">惡意</a><img src="x" onerror="alert(1)">';
$clean = $purifier->purify($dirty);
echo $clean;
// 輸出:

合法

惡意 ?>

3. 場景化選型指南

業務場景推薦方案代碼片段
用戶名、標籤、搜索關鍵詞strip_tags + 長度限制substr(strip_tags($input), 0, 50)
評論、留言(純文本)htmlspecialchars 輸出時轉義模板中 {$comment|escape}
簡單公告、備註(允許粗體、連結)strip_tags($input, '<b><i><a>')配合 rel="nofollow" 過濾 a 標籤
富文本編輯器內容(文章、帖子、商品詳情)HTMLPurifier配置白名單、CSS 白名單、緩存
API 輸出 JSON 供前端渲染後端存原文/HTMLPurifier 淨化版,前端 DOMPurify 二次防護雙重防護纏繞策略

4. 完整防護鏈:輸入過濾 + 存儲 + 輸出轉義

<?php
class ContentSecurity {
    private HTMLPurifier $purifier;
    
    public function __construct() {
        $config = HTMLPurifier_Config::createDefault();
        $config->set('HTML.Allowed', 'p,b,i,u,em,strong,a[href|title|rel],ul,ol,li,br,img[src|alt|width|height],blockquote,pre,code,h1,h2,h3,h3,h4,h5,h6');
        $config->set('CSS.AllowedProperties', 'font-weight,font-style,text-decoration,color,background-color,text-align,margin,padding,border');
        $config->set('Attr.EnableID', true);
        $config->set('Cache.SerializerPath', sys_get_temp_dir() . '/htmlpurifier');
        $this->purifier = new HTMLPurifier($config);
    }
    
    /** 入庫前淨化 */
    public function purifyForStorage(string $html): string {
        return $this->purifier->purify($html);
    }
    
    /** 輸出到 HTML 頁面(雙重保險) */
    public function escapeForOutput(string $html): string {
        // 若已信任存儲內容,可直接輸出
        // 若不確定,再次淨化或轉義
        return htmlspecialchars($html, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
    
    /** API 返回 JSON */
    public function forApi(string $html): string {
        return $html; // 前端負責 DOMPurify
    }
}

// 使用
$security = new ContentSecurity();
$raw = $_POST['content'] ?? '';
$clean = $security->purifyForStorage($raw);
// 存入資料庫 $clean
// 前端渲染時:{!! $post->content !!} (Laravel Blade 信任輸出) 或 DOMPurify.sanitize()
?>

5. 前端輔助:DOMPurify

<!-- 頁面引入 -->
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.0/dist/purify.min.js"></script>

<script>
function renderUserContent(rawHtml, containerId) {
    const clean = DOMPurify.sanitize(rawHtml, {
        ALLOWED_TAGS: ['p','b','i','u','em','strong','a','ul','ol','li','br','img','span','div','blockquote','pre','code','h1','h2','h3','h4','h5','h6'],
        ALLOWED_ATTR: ['href','title','rel','src','alt','width','height','style','class','id'],
        ALLOWED_URI_REGEXP: /^(?:https?|mailto):/i,
    });
    document.getElementById(containerId).innerHTML = clean;
}
</script>

6. 性能優化建議

  1. 啟用 HTMLPurifier 緩存:Cache.SerializerPath 指向可寫目錄,首次解析後序列化,後續直接反序列化,速度提升 10-50 倍
  2. 異步淨化:用戶提交後立即返回,後台隊列處理淨化,完成後推送更新(WebSocket/輪詢)
  3. 3. 分級處理:高風險字段(文章正文)用 HTMLPurifier,低風險(標題、摘要)用 strip_tags 4. OpCache + JIT:PHP 8.1+ 開啟 JIT 可顯著提升 HTMLPurifier 解析速度

總結

永遠不要信任用戶輸入。根據業務場景選型:

  • 純文本 → htmlspecialchars 輸出時轉義
  • 極簡富文本 → strip_tags 白名單 + 輸出轉義
  • 完整富文本 → HTMLPurifier(後端)+ DOMPurify(前端) 雙重防護

記住:strip_tags 不是安全函數,htmlspecialchars 是輸出層最後防線,HTMLPurifier 才是存儲層真正的淨化器。

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