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

PHP 進階 HTML 過濾:保留指定標籤與屬性的白名單實作

     編輯:PHP 相關知識

需求場景

使用者輸入富文本 (如編輯器內容),需**只保留**特定標籤 (如 ``, `

`, ``) 及其**特定屬性** (如 `href`, `target`, `src`, `alt`),移除所有其他標籤、屬性、Script、事件處理器。

核心邏輯 (基於原文函式重構)

  1. 使用正則找出所有目標標籤 (如 `...`)。
  2. 對每個標籤,解析屬性鍵值對。
  3. 根據白名單保留屬性,重組標籤字串。
  4. 替換原字串中對應標籤。

現代化實作 (PHP 8.0+、DOMDocument 方案,更健壯)

<?php
function filterHtmlAllowlist(string $html, array $allowlist): string {
    // 允許清單格式: ['a' => ['href','target','rel'], 'img' => ['src','alt','width','height'], 'p' => ['class']]
    $dom = new DOMDocument();
    // 載入片段,避免自動補全 html/body
    $dom->loadHTML('<meta charset="utf-8">' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

    $xpath = new DOMXPath($dom);
    // 遍歷所有元素
    foreach ($xpath->query('//*') as $node) {
        $tag = strtolower($node->nodeName);
        if (!isset($allowlist[$tag])) {
            // 標籤不在白名單:移除節點,但保留子節點 (避免內容消失)
            $parent = $node->parentNode;
            while ($node->childNodes->length > 0) {
                $parent->insertBefore($node->childNodes->item(0), $node);
            }
            $parent->removeChild($node);
            continue;
        }

        // 標籤在白名單:過濾屬性
        $allowedAttrs = $allowlist[$tag];
        foreach ($node->attributes as $attr) {
            if (!in_array($attr->nodeName, $allowedAttrs, true)) {
                $node->removeAttribute($attr->nodeName);
            }
        }
        // 強制安全屬性
        if ($tag === 'a' && !$node->hasAttribute('rel')) {
            $node->setAttribute('rel', 'noopener noreferrer');
        }
    }

    // 序列化 body 內容
    $body = $dom->getElementsByTagName('body')->item(0);
    if (!$body) return ''; // 理論上不會發生
    $result = '';
    foreach ($body->childNodes as $child) {
        $result .= $dom->saveHTML($child);
    }
    return $result;
}

// 使用範例
$allowlist = [
    'a' => ['href', 'target', 'rel', 'title'],
    'img' => ['src', 'alt', 'width', 'height', 'loading'],
    'p' => ['class', 'style'],
    'br' => [],
    'strong' => [], 'em' => [], 'u' => [], 'span' => ['style'],
    'ul' => [], 'ol' => [], 'li' => [],
];

$dirty = '<script>alert(1)</script><a href="https://evil.com" onclick="steal()" target="_blank">Link</a><img src="x" onerror="alert(1)">';
$clean = filterHtmlAllowlist($dirty, $allowlist);
// 結果: Link
?>

為什麼不用正則?

  • HTML 不是正則語言:巢狀、屬性值含引號/尖括號、註釋、CDATA、條件註釋、實體編碼... 正則**無法**完整處理。
  • 瀏覽器解析器極其寬容,正則與瀏覽器行為不一致導致 XSS 繞過。
  • DOMDocument 使用瀏覽器同級解析邏輯 (libxml2),語義正確。

效能優化

  • 大量請求時,HTML Purifier 內建快取機制 (序列化定義物件)。
  • 可將 `$allowlist` 定義為靜態常數,避免重複建構。
  • 極高併發考慮:卸載至 Worker (Queue) 非同步處理,或使用更快的 C 擴展 (如 `htmlpurifier` 的 `lexer` 選項)。

總結

白名單過濾是富文本安全的核心。**請使用 DOMDocument 或 HTML Purifier 等成熟解析器**,勿自寫正則。定義清晰的標籤/屬性白名單,強制安全屬性 (`rel=noopener`),配合 CSP 標頭,構建完整防禦體系。

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