HTML-дезинфицирующее средство с использованием регулярных выражений и проблем безопасности - PullRequest
1 голос
/ 22 декабря 2010

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

Я хотел бы показать вам мой код ниже (написанный на PHP 5.2). Кажется, все работает нормально, но мне все еще интересно, есть ли проблемы с безопасностью.

Итак, я что-то не так понял?

Основной принцип - использовать Html_Sanitizer :: sanitize ()

  1. Функция сначала заменяет разрешенные теги без атрибутов токенами. Затем выполните синтаксический анализ тегов с атрибутами и замените их токеном.
  2. Затем HTML-теги анализируются для обнаружения разрешенных атрибутов (с использованием функции cleanTag). Поэтому HTML-тег перестраивается безопасным способом (давайте надеяться).
  3. htmlspecialchars используется, чтобы убедиться, что оставшийся код чист.
  4. токены заменены на безопасные теги.

Код:

class Html_Sanitizer
{
    const VALIDATOR_CSS_UNIT = '(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0';
    const VALIDATOR_URL = 'http://\\S+';
    const VALIDATOR_CSS_PROPERTY = '[a-z\-]+';
    const VALIDATOR_STYLE = '[^"]*';

    protected static $_tags = 'a|b|blockquote|br|cite|d[ldt]|h[1-6]|i|img|li|ol|p|span|strong|u|ul';

    protected static $_attributes = array(
        'img' => array(
            'width' => '[0-9]+',
            'height' => '[0-9]+',
            'src' => self::VALIDATOR_URL,
            'style' => self::VALIDATOR_STYLE
            ),
        'span' => array(
            'style' => self::VALIDATOR_STYLE
            ),
        'p' => array(
            'style' => self::VALIDATOR_STYLE
            ),
        'a' =>  array(
            'href' => self::VALIDATOR_URL
            )
    );

    protected static $_styleValidators = array(
        'color' => '(\#[a-fA-F0-9]+)|([a-z ]+)',
        'background-color' => '\#[a-zA-Z0-9]+',
        'font-style' => '(normal|italic|oblique)',
        'font-size' => '[\-a-z]+',
        'margin-left' => self::VALIDATOR_CSS_UNIT,
        'margin-right' => self::VALIDATOR_CSS_UNIT,
        'text-align' => '(left|right|center|justify)',
        'text-indent' => self::VALIDATOR_CSS_UNIT,
        'text-decoration' => '(none|overline|underline|blink|line-through)',
        'width' => self::VALIDATOR_CSS_UNIT,
        'height' => self::VALIDATOR_CSS_UNIT
    );

    public static function sanitize($str)
    {
        $tokens = array();

        //tokenize opening tags with no attributes
        $pattern = '#<(/)?('. self::$_tags .')>#';
        $replace = '__SAFE_TAG_$1$2__';
        $str = preg_replace($pattern, $replace, $str);

        // tokenize tags with attributes
        $pattern = '#<('. self::$_tags .')(?:\s+(?:[a-z]+)="(?:[^"\\\]*(?:\\\"[^"\\\]*)*)")*\s*(/)?>#';
        preg_match_all($pattern, $str, $matches, PREG_SET_ORDER);
        foreach($matches as $i => $match) {
            $tokens[$i] = self::cleanTag($match[1], $match[0]);
            $str = str_replace($match[0], '__SAFE_TOKEN_'.$i.'__', $str);
        }

        $str = htmlspecialchars($str);

        foreach ($tokens as $i => $cleanTag) {
            $str = str_replace('__SAFE_TOKEN_'.$i.'__', $cleanTag, $str);
        }

        $pattern = '#__SAFE_TAG_(/?(?:'. self::$_tags .'))__#';
        $replace = '<$1>';
        $str = preg_replace($pattern, $replace, $str);

        return $str;
    }

    public static function cleanTag($tag, $str)
    {
        $cleanTag = '<' . $tag;

        if ($tag === 'a') {
            $cleanTag .= ' rel="nofolow" target="_blank"';
        }

        if (isset(self::$_attributes[$tag])) {
            foreach(self::$_attributes[$tag] as $attr => $attrPattern) {
                $pattern = '#'.$attr.'="('. $attrPattern .')"#';
                preg_match($pattern, $str, $match);
                if (isset($match[1])) {
                    if ($attr == 'style') {
                        $cleanTag .= ' style="' . self::cleanStyle($match[1]) . '"';
                    } else {
                        $cleanTag .= ' ' . $attr . '="' . $match[1] . '"';
                    }
                }
            }
        }

        if ($tag === 'img') {
            $cleanTag .= ' /';
        }

        $cleanTag .= '>';
        return $cleanTag;
    }

    public static function cleanStyle($style)
    {
        $cleanStyle = '';

        foreach(self::$_styleValidators as $stl => $stlPattern) {
            $pattern = '#[; ]?' . $stl . '\s*:\s*(' . $stlPattern . ')\s*;#i';
            preg_match($pattern, $style, $match);
            if (isset($match[1])) {
                $cleanStyle .= ($cleanStyle ? ' ' : '') . $stl . ':' . $match[1] . ';';
            }
        }

        return $cleanStyle;
    }
}

1 Ответ

0 голосов
/ 22 декабря 2010

Я могу использовать атрибуты, связанные со стилем, чтобы портить ваш сайт.И в некоторых браузерах, чтобы вызвать сбой браузера, установив огромную комбинацию ширины и высоты.

Я могу использовать стили, чтобы сделать еще больше искажения.

Я могу использовать атрибут изображения SRC для отслеживаниядвижения пользователей.Чтобы шпионить за ними и зарабатывать деньги.

Это все, что я могу сделать с вашим классом, если вы закодировали его ИДЕАЛЬНО, как предполагалось.Каких шансов у вас нет, и, вероятно, есть другие дыры, которые открывают доступ к еще более коварным вещам.

...