Разрешить отправленный пользователем HTML в PHP

Я хочу разрешить множество пользовательских html-файлов для профилей пользователей, в настоящее время я пытаюсь отфильтровать то, что мне не нужно, но теперь я хочу изменить и использовать подход белого списка.

Вот мой текущий подход без белого списка

function FilterHTML($string) {
    if (get_magic_quotes_gpc()) {
        $string = stripslashes($string);
    }
    $string = html_entity_decode($string, ENT_QUOTES, "ISO-8859-1");
    // convert decimal
    $string = preg_replace('/&#(\d+)/me', "chr(\\1)", $string); // decimal notation
    // convert hex
    $string = preg_replace('/&#x([a-f0-9]+)/mei', "chr(0x\\1)", $string); // hex notation
    //$string = html_entity_decode($string, ENT_COMPAT, "UTF-8");
    $string = preg_replace('#(&\#*\w+)[\x00-\x20]+;#U', "$1;", $string);
    $string = preg_replace('#(<[^>]+[\s\r\n\"\'])(on|xmlns)[^>]*>#iU', "$1>", $string);
    //$string = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $string); //bad line
    $string = preg_replace('#/*\*()[^>]*\*/#i', "", $string); // REMOVE /**/
    $string = preg_replace('#([a-z]*)[\x00-\x20]*([\`\'\"]*)[\\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iU', '...', $string); //JAVASCRIPT
    $string = preg_replace('#([a-z]*)([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iU', '...', $string); //VBSCRIPT
    $string = preg_replace('#([a-z]*)[\x00-\x20]*([\\\]*)[\\x00-\x20]*@([\\\]*)[\x00-\x20]*i([\\\]*)[\x00-\x20]*m([\\\]*)[\x00-\x20]*p([\\\]*)[\x00-\x20]*o([\\\]*)[\x00-\x20]*r([\\\]*)[\x00-\x20]*t#iU', '...', $string); //@IMPORT
    $string = preg_replace('#([a-z]*)[\x00-\x20]*e[\x00-\x20]*x[\x00-\x20]*p[\x00-\x20]*r[\x00-\x20]*e[\x00-\x20]*s[\x00-\x20]*s[\x00-\x20]*i[\x00-\x20]*o[\x00-\x20]*n#iU', '...', $string); //EXPRESSION
    $string = preg_replace('#</*\w+:\w[^>]*>#i', "", $string);
    $string = preg_replace('#</?t(able|r|d)(\s[^>]*)?>#i', '', $string); // strip out tables
    $string = preg_replace('/(potspace|pot space|rateuser|marquee)/i', '...', $string); // filter some words
    //$string = str_replace('left:0px; top: 0px;','',$string);
    do {
        $oldstring = $string;
        //bgsound|
        $string = preg_replace('#</*(applet|meta|xml|blink|link|script|iframe|frame|frameset|ilayer|layer|title|base|body|xml|AllowScriptAccess|big)[^>]*>#i', "...", $string);
    } while ($oldstring != $string);
    return addslashes($string);
}

Вышеприведенное работает очень хорошо, у меня никогда не было проблем после 2 лет использования, но для подхода с белым списком есть ли что-нибудь похожее на метод stackoverflows C #, но в PHP? http://refactormycode.com/codes/333-sanitize-html


person JasonDavis    schedule 04.09.2009    source источник


Ответы (7)


HTML Purifier – это библиотека HTML-фильтров, соответствующая стандартам, написанная на PHP. HTML Purifier не только удалит весь вредоносный код (более известный как XSS) с помощью тщательно проверенного, безопасного, но разрешительного белого списка, но также обеспечит соответствие ваших документов стандартам, что достижимо только при всестороннем знании спецификаций W3C.

person raspi    schedule 04.09.2009
comment
Используя php, это действительно правильный путь. Его продукция удивительна и безопасна. - person DGM; 04.09.2009
comment
Я видел это раньше, я думал, что это действительно громоздко, хотя я проверю это снова, спасибо - person JasonDavis; 04.09.2009
comment
Наткнулся на то, что мне было нужно, и искал около получаса, пока не наткнулся на ваш пост! :-) Спасибо - person Liam Wheldon; 09.08.2017

Может быть, безопаснее использовать DOMDocument, чтобы правильно проанализировать его, удалить запрещенные теги с помощью removeChild(), а затем получить результат. Не всегда безопасно фильтровать данные с помощью регулярных выражений, особенно если они становятся настолько сложными. Хакеры могут найти способ обмануть ваши фильтры, форумы и социальные сети прекрасно об этом знают.

Например, браузеры игнорируют пробелы после ‹. Ваш фильтр регулярных выражений ‹скрипт, но если я использую ‹ скрипт... большая НЕУДАЧА!

person Havenard    schedule 04.09.2009

HTML Purifier — лучший анализатор/очиститель HTML.

person MiffTheFox    schedule 04.09.2009

Для тех из вас, кто предлагает просто использовать strip_tags... имейте в виду: strip_tags НЕ удалит атрибуты тегов и неработающие теги. тоже испортит.

Со страницы руководства:

Предупреждение Поскольку strip_tags() на самом деле не проверяет HTML, частичные или неработающие теги могут привести к удалению большего количества текста/данных, чем ожидалось.

Предупреждение Эта функция не изменяет какие-либо атрибуты тегов, которые вы разрешаете использовать с помощью allowable_tags , включая атрибуты style и onmouseover, которыми недобросовестный пользователь может злоупотреблять при публикации текста, который будет показан другим пользователям.

Вы НЕ МОЖЕТЕ полагаться только на это одно решение.

person Alt-Rock Ninja Cowgirl    schedule 04.09.2009

Вы можете просто использовать функцию strip_tags()

Поскольку функция определяется как

string strip_tags  ( string $str  [, string $allowable_tags  ] )

Ты можешь сделать это:

$html = $_POST['content'];
$html = strip_tags($html, '<b><a><i><u><span>');

Но обратите внимание, что с помощью strip_tags вы не сможете отфильтровать атрибуты. например

<a href="javascript:alert('haha caught cha!');">link</a>
person mauris    schedule 04.09.2009

Попробуйте эту функцию «getCleanHTML» ниже, извлеките текстовое содержимое из элементов, за исключением элементов с именем тега в белом списке. Этот код чистый и простой для понимания и отладки.

<?php

$TagWhiteList = array(
    'b', 'i', 'u', 'strong', 'em', 'a', 'img'
);

function getHTMLCode($Node) {
    $Document = new DOMDocument();    
    $Document->appendChild($Document->importNode($Node, true));
    return $Document->saveHTML();
}
function getCleanHTML($Node, $Text = "") {
    global $TagWhiteList;

    $TextName = $Node->tagName;
    if ($TextName == null)
        return $Text.$Node->textContent;

    if (in_array($TextName, $TagWhiteList)) 
        return $Text.getHTMLCode($Node);

    $Node = $Node->firstChild;
    if ($Node != null)
        $Text = getCleanHTML($Node, $Text);

    while($Node->nextSibling != null) {
        $Text = getCleanHTML($Node->nextSibling, $Text);
        $Node = $Node->nextSibling;
    }
    return $Text;
}

$Doc = new DOMDocument();
$Doc->loadHTMLFile("Test.html");
echo getCleanHTML($Doc->documentElement)."\n";

?>

Надеюсь это поможет.

person NawaMan    schedule 04.09.2009

На самом деле это довольно простая цель - вам просто нужно проверить все, что НЕ является некоторыми тегами из списка тегов, внесенных в белый список, и удалить их из источника. Это можно сделать довольно легко с помощью одного регулярного выражения.

function sanitize($html) {
  $whitelist = array(
    'b', 'i', 'u', 'strong', 'em', 'a'
  );

  return preg_replace("/<(^".implode("|", $whitelist).")(.*)>(.*)<\/(^".implode("|", $whitelist).")>/", "", $html);
}

Я не проверял это, и, вероятно, где-то там есть ошибка, но вы понимаете, как это работает. Вы также можете рассмотреть возможность использования языка форматирования, такого как Textile или Markdown.

Джейми

person Jamie Rumbelow    schedule 04.09.2009