помогите проверить безопасность html парсера

Discussion in 'Безопасность и Анонимность' started by HAP3AH, 28 Sep 2011.

  1. HAP3AH

    HAP3AH New Member

    Joined:
    19 Mar 2011
    Messages:
    2
    Likes Received:
    0
    Reputations:
    0
    написал парсер html тегов для сайта. Помогите проверить уязвимости. Наверное написан коряво... ну я пхп не долго изучаю.

    Code:
    class chu_parser {
    
        function parse($text) {
            $text = htmlspecialchars(trim(str_replace("\0", "", $text)), ENT_NOQUOTES);
    
            $zapret_params = array("test", "codetype", 'style', 'action', 'onAbort', 'onBlur', 'onChange', 'onClick', 'onDblClick', 'onError', 'onFocus', 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onLoad', 'onMouseDown', 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp', 'onMove', 'onReset', 'onResize', 'onSelect', 'onSubmit', 'onUnload'); // плохие параметры
    
            $bad_attrvals = array("javascript:", "data:", "test:", "behavior:", "vbscript:", "mocha:", "livescript:", "shadow", "shbox", "shinfo", "sherror", "left", "right", "menulab", "menu2", "detached"); // плохие значения параметров
            $bad_attrvals_str = $this->arr2str($bad_attrvals, '|'); // подготовим для preg_match   
            $allow_tags = array('b', 'div', 'hr', 'img', 'code', 'bkb', 'tt', 'table', 'tr', 'td', 'font', 'center', 'link', 'embed', 'object', 'quote', 'marquee',
                'sup', 'sup', 'effect', 'param', 'i', 'li', 'ol', 's', 'u', 'blink');
            $pattern = "#<([a-z]+)( .*)?(?!/)>#iU";
    
            preg_match_all($pattern, $text, $matches);
            $all_full_tags = $matches[0]; //все полные теги
            foreach ($matches[1] as $value) {
                $new_tags_params[][$value] = array();
            }
            for ($k = 0; $k < count($matches[2]); $k++) {
                $value = $matches[2][$k];
                // заменям кавычки на стандартные
                $value = str_ireplace("'", '"', $value);
                // удаляем лишние пробелы
                $value = str_ireplace(' ', '', $value);
                $value = str_ireplace('"', '" ', $value);
                $value = str_ireplace('=\" ', '=\"', $value);
                //================
                $val_arr = $this->del_null_in_arr(explode(' ', $value));
                $new_2_val_arr = array();
                if ($val_arr) {
                    foreach ($val_arr as $val) {
                        $new_val_arr = $this->del_null_in_arr(explode('=', $val));
                        if ($new_val_arr) {
                            $new_val_arr[1] = str_ireplace('\"', '', $new_val_arr[1]);
                            //$new_val_arr[1] = str_ireplace("\'", '', $new_val_arr[1]);
                            $new_2_val_arr[$new_val_arr[0]] = $new_val_arr[1];
                        }
                    }
                }
                $new_tags_params[$k][$matches[1][$k]] = $new_2_val_arr;
            }
    //======= теперь тут нормальный архив с тегами, затем проверяем разрешенность тегов и параметров
            if ($new_tags_params) {
                for ($k = 0; $k < count($new_tags_params); $k++) {
                    foreach ($new_tags_params[$k] as $tag_name => $param_arr) {
                        if (!in_array($tag_name, $allow_tags))
                            $new_tags_params[$k][] = 'banned'; // если тега нет среди разрешенных
                        if ($param_arr) {
                            foreach ($param_arr as $param_name => $param_value) {
                                if (in_array($param_name, $zapret_params) || preg_match("/($bad_attrvals_str)/i", $param_value))
                                    $new_tags_params[$k][] = 'banned'; // если параметр тега или его значение запрещено
                            }
                        }
                    }
                }
    
    
    
    //============= теперь создаём обратно теги с параметрами
                for ($k = 0; $k < count($new_tags_params); $k++) {
                    foreach ($new_tags_params[$k] as $tag_name => $param_arr) {
                        if ($tag_name != '0' && $tag_name != '1') {
                            $tag = "<$tag_name";
                            if ($param_arr) {
                                foreach ($param_arr as $param_name => $param_value) {
                                    $tag .= " $param_name=\"$param_value\"";
                                }
                            }
                            $tag.=">";
                            if ($new_tags_params[$k][0] == 'banned') {
                                $tag = htmlspecialchars($tag);
                            }
                            $new_tags[0][$k] = $tag;
                            if ($new_tags_params[$k][0] != 'banned') {
                                $new_tags[1][$k] = $tag_name;
                            }
                        }
                    }
                }
    //============ теперь вставляем обратно теги
                if ($new_tags) {
                    //$html_new_tags = htmlentities_arr($new_tags);
                    for ($k = 0; $k < count($new_tags[0]); $k++) {
                        $text = str_ireplace($all_full_tags[$k], $new_tags[0][$k], $text);
                    }
                }
    
                //============= теперь закрываем все теги
                preg_match_all("#&lt;/([a-z]+)&gt;#iU", $text, $matches); // все закрывающие теги
                for ($k = 0; $k < count($matches[1]); $k++) {
                    if (in_array($matches[1][$k], $new_tags[1])) {
                        $num = 1;
                        $search = array_search($matches[1][$k], $new_tags[1]);
                        $text = str_replace($matches[0][$k], '</' . $new_tags[1][$search] . '>', $text, $num);
                        unset($new_tags[1][$search]);
                    }
                }
                //==========================
            }
            return $text;
        }
    
        function del_null_in_arr($arr) {
            foreach ($arr as $value) {
                if (trim($value) != '') {
                    $new_arr[] = $value;
                }
            }
            return $new_arr;
        }
    
    //делает из массива строку с разделителями
        function arr2str($arr, $str) {
            for ($i = 0; $i < count($arr); $i++) {
                if ($i + 1 < count($arr)) {
                    $new_str .= $arr[$i] . $str;
                } else {
                    $new_str .= $arr[$i];
                }
            }
            return $new_str;
        }
    
    }
     
  2. scanislav

    scanislav Elder - Старейшина

    Joined:
    25 Jun 2010
    Messages:
    87
    Likes Received:
    22
    Reputations:
    31
    Лучше вообше запретить все атрибуты начинающиеся на on иначе за всеми не уследишь. onmouseenter onscroll например пропущены

    и еще оно кучу ошибок выдает, из-за необъявленных переменных. несмертельно но тестировать тяжело.
     
  3. HAP3AH

    HAP3AH New Member

    Joined:
    19 Mar 2011
    Messages:
    2
    Likes Received:
    0
    Reputations:
    0
    ну у меня на денвере ошибок из-за переменных нет, но объявлю все, спасибо.
    ещё нашел, что теги не закрытые юзером не закрываются.