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

PHP 過濾 HTML 標籤屬性類別:保留白名單、例外與忽略標籤

     編輯:PHP 綜合知識

在處理使用者輸入的 HTML 內容時,為了防止 XSS 攻擊或排版混亂,我們通常需要移除不安全或不需要的屬性(如 onclick、style、onload 等)。本文介紹一個 HtmlAttributeFilter 類別,它能依據「允許清單」、「標籤例外規則」與「忽略標籤」三種策略,精準過濾 HTML 標籤屬性,保留合法內容。

類別核心功能

  • strip($str):主入口,回傳過濾後的 HTML 字串。
  • setAllow(array $attrs):全域允許保留的屬性(如 id、class、title)。
  • setException(array $rules):針對特定標籤允許額外屬性(如 a 標籤保留 href、ul 保留 class)。
  • setIgnore(array $tags):完全跳過過濾的標籤(如 img 保留所有屬性)。

運作流程

  1. 小寫化:將輸入字串轉為小寫,統一處理。
  2. 尋找元素:使用正則 `<([^ !/>\\n]+)([^>]*)>` 擷取所有標籤名稱與屬性字串,排除忽略清單中的標籤。
  3. 解析屬性:對屬性字串用正則 `([^ =]+)\\s*=\\s*[\"|']{0,1}([^\"']*)[\"|']{0,1}` 拆解成 name/value 陣列。
  4. 判斷保留:屬性若在全域允許清單,或符合該標籤的例外規則,則保留;其餘移除。
  5. 重建標籤:將保留的屬性重新組合,以 preg_replace 替換原標籤。

完整類別程式碼

<?php
class HtmlAttributeFilter {
    private $_str = ''; private $_allow = array(); private $_exception = array(); private $_ignore = array();

    public function strip($str) {
        $this->_str = $str;
        if (is_string($this->_str) && strlen($this->_str) > 0) {
            $this->_str = strtolower($this->_str);
            $res = $this->findElements();
            if (is_string($res)) return $res;
            $nodes = $this->findAttributes($res);
            $this->removeAttributes($nodes);
        }
        return $this->_str;
    }

    public function setAllow($param = array()) { $this->_allow = $param; }
    public function setException($param = array()) { $this->_exception = $param; }
    public function setIgnore($param = array()) { $this->_ignore = $param; }

    private function findElements() {
        $nodes = array();
        preg_match_all("/<([^ !\\/>\\n]+)([^>]*)>/i", $this->_str, $elements);
        foreach ($elements[1] as $k => $tag) {
            if ($elements[2][$k]) {
                if (!in_array($tag, $this->_ignore)) {
                    $nodes[] = array('literal'=>$elements[0][$k], 'name'=>$tag, 'attributes'=>$elements[2][$k]);
                }
            }
        }
        return empty($nodes) ? $this->_str : $nodes;
    }

    private function findAttributes($nodes) {
        foreach ($nodes as &$node) {
            preg_match_all("/([^ =]+)\\s*=\\s*[\"|']{0,1}([^\"']*)[\"|']{0,1}/i", $node['attributes'], $atts);
            $node['attributes'] = array();
            if ($atts[1]) {
                foreach ($atts[1] as $ak => $an) {
                    $node['attributes'][] = array('literal'=>$atts[0][$ak], 'name'=>$an, 'value'=>$atts[2][$ak]);
                }
            }
        }
        return $nodes;
    }

    private function removeAttributes($nodes) {
        foreach ($nodes as $node) {
            $newAttrs = '';
            if (is_array($node['attributes'])) {
                foreach ($node['attributes'] as $att) {
                    if (in_array($att['name'], $this->_allow) || $this->isException($node['name'], $att['name'])) {
                        $newAttrs = $this->createAttributes($newAttrs, $att['name'], $att['value']);
                    }
                }
            }
            $replacement = $newAttrs ? "<{$node['name']} $newAttrs>" : "<{$node['name']}>";
            $this->_str = preg_replace('/' . $this->protect($node['literal']) . '/', $replacement, $this->_str);
        }
    }

    private function isException($tag, $attr) {
        return isset($this->_exception[$tag]) && in_array($attr, $this->_exception[$tag]);
    }
    private function createAttributes($str, $name, $value) {
        return $str . ($str ? ' ' : '') . "$name=\"$value\"";
    }
    private function protect($str) {
        $map = array('^'=>'\\^','['=>'\\[','.'=>'\\.','$'=>'\\$','{'=>'\\{','*'=>'\\*','('=>'\\(','\\'=>'\\\\','/'=>'\\/','+'=>'\\+',')'=>'\\)','|'=>'\\|','?'=>'\\?','<'=>'\\<','>'=>'\\>');
        return strtr($str, $map);
    }
}
?>

實際應用範例

<?php
require('HtmlAttributeFilter.class.php');

$html = '<div class="bd" id="main" onclick="alert(1)"><a href="https://example.com" title="link">連結</a><img src="pic.jpg" width="100"></div>';

$filter = new HtmlAttributeFilter();
$filter->setAllow(array('id', 'class'));           // 全域允許 id, class
$filter->setException(array('a' => array('href', 'title'))); // a 標籤額外允許 href, title
$filter->setIgnore(array('img'));                   // img 完全不處理

echo '原始: ' . htmlspecialchars($html) . '<br><br>';
echo '過濾後: ' . htmlspecialchars($filter->strip($html));
?>

輸出結果

原始: <div class="bd" id="main" onclick="alert(1)"><a href="https://example.com" title="link">連結</a><img src="pic.jpg" width="100"></div>

過濾後: <div class="bd" id="main"><a href="https://example.com" title="link">連結</a><img src="pic.jpg" width="100"></div>

div 保留 class、id,移除 onclick;a 保留 href、title;img 完全保持原樣。此類別靈活且易擴充,適合 CMS、論壇、留言板等需嚴格控制 HTML 輸出的場景。

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