удаление ненужных HTML тегов из пользовательского ввода.

    Вариант реализации



    Я тут подумал… Чтобы без геммороя удалить из HTML кода все ненужные (читай опасные) HTML теги, его вовсе не обязательно парсить в классическом понимании этого слова, а можно просто преобразовать в DOMDocument на сервере. Тогда нам будут доступны замечательные медоты removeChild и removeAttribute.

    Сделать это можно примерно так:

    public function process($text) {
    $document = new DOMDocument('1.0', 'utf-8');
    @$document->loadHTML($text);
    $this->cleanNode($document->documentElement);
    return $this->prepare($document->saveXML());
    }


    реализацию cleanNode оставляю на ваше усмотрение, так как что русскому хорошо, то немцу смерть. :)
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 86

      +4
      А мысль-то свежа...
        0
        так еще можно добиться w3c валидности
          0
          можно просто преобразовать в DOMDocument

          А что если будет два неверно вложенных тега..?
            +1
            Будет неверная структура HTML документа, цель не в том, чтобы верно распарсить, а в том чтобы упростить удаление опасных тегов и аттрибутов. Ведь, как правило, на входе в такой скрипт стоит вменямый WYSIWYG редактор.
            0
            Предварительно tidy надо бы ещё применить, а то парсинг не пройдет
              0
              Если вы обратили внимание, то используется метод loadHTML, который не генерит fatal, а только warning, которые подавляются оператором @.
            • UFO just landed and posted this here
                0
                1. strip_tags не могут удалить у
                2. регулярки для вырезания тегов выглядят очень устрашающе.
                3. в PHP5 DOM встроен в ядро PHP, в отличие от SafeHTML.

                А здесь мы имеем построенный (распарсеный) на сервере DOM, из которого мы удобно удаляем все что нам не надо, не боясь что регулярка пропустит какую-то новую хитрую HTML инструкцию.
                  0
                  strip_tags не могут удалить <img src="..." onclick="..." />
                    0
                    еще как могут))
                    $text = "<b>Перед картинкой</b><img src=\"...\" onclick=\"...\" /><i>После картинки</i><br /><a href=\"..\" Не закрытый тег ссылки</a>";
                    echo strip_tags($text,", ");
                    вот такое выводит: Перед картинкойПосле картинки,
                    а с не закрытыми тегами уже не спавляется..
                      0
                      <b>Перед картинкой</b><i>После картинки</i>
                      • UFO just landed and posted this here
                          0
                          если так, то да, непосильная задача для strip_tags.
                          а лучше всего ИМХО решить эту задачу на уровне редактора.
                          0
                          я имел ввибду, что удаление onclick
                        –1
                        А там на сервере дом парсится не регулярками штоле, а волшебным заклятьем? У волшебного заклятья есть 100% гарантия надежности?
                          0
                          Опять двадцать пять....
                          Самоцель этого метода не в том чтобы починить неверный HTML, а в том чтобы упростить процедуру удаления из него опасных элементов.

                          1. Для адекватной работы такого подхода мы должны иметь более менее нормально размеченый HTML на входе. Видимо пришедший в WYSIWYG редактора.

                          2. Первичный DOM строится в памяти сервера и никакие опасные конструкции не могут там ничего сделать. Затем он очищается от ненужных тегов и аттрибутов, при необходимости модифицируется, а затем сохраняется как XML.
                        0
                        Вы в самом деле считаете, что регулярными выражениями можно удалить теги из документа?
                        • UFO just landed and posted this here
                        0
                        DOM — это хорошо, если б ещё без багов. Впрочем, с такой простой задачей DOM-расширение PHP5 вполне справится.

                        Нужно ещё не забыть перед созданием DOM-документа добавить в начало исходного HTML-кода корректный DOCTYPE, дабы на выходе не появилось сюрпризов типа <br></br>, <hr></hr> и т. д.
                          0
                          про DOCTYPE, так как используется saveXML, то

                          , полностью исключены.
                            0
                            <br></br> <hr></hr>
                              +1
                              Я б не писал, если б не сталкивался. ;-)
                                0
                                так я проверил прежде чем писать. :)
                                  0
                                  Сейчас, к сожалению, указанный мною результат воспроизвести не могу, но в процессе экспериментов с DOM-расширением он точно проскакивал. В данном же случае без DOCTYPE происходит не менее некорректное закрытие пустых элементов в XML-стиле:

                                  <div></div> → <div/>
                                  <p></div> → <p/>
                                  и т.д.

                                  После добавления DOCTYPE элементы начинают закрываться корректно (в соответствии с DTD): <br />, <hr />, но <div></div>, <p></p> и т. д.
                                    0
                                    очепятка:
                                    <p></p> → <p/>
                                      0
                                      А зачем вам пустые дивы и абазацы? В моей cleanNode я просто их удаляю.
                                      Но за информацию про DOCTYPE спасибо, позже я добавлю ее в текст топика.
                                        0
                                        Пустой элемент может иметь функцию сброса обтекания объектов в области контента. И если на уровне шаблонов такой сброс давно делается куда более изящно (правилами для родительского элемента), то в контенте пустой сбрасывающий элемент используется довольно часто.
                                          0
                                          Так вы шаблонизацию проводите после получения контента от пользователя. Пользовательский инпут - это текст, а не разметка.
                                            0
                                            Это лишь пример. В любом случае лучше проблем себе не создавать вовсе, чем героически их решать. ;-)
                            0
                            его вовсе не обязательно парсить

                            а что, как вы думаете, делает объект DOMDocument? Не парсит? Угадывает?
                              0
                              хотя, конечно, если закрыть глаза на производительность…
                                0
                                Да, десятиэтажные регулярки одназначно работают быстрее скомпиленного кода.
                                  –1
                                  PCRE куда более мощнее, а в прямых руках и быстрее, чем DOM парсеры.
                                  PCRE как ядерные боеголовки. В нужных руках они могут пылиться без действий, но при этом работать.
                                    –3
                                    Я так понимаю, вы на них дрочите.
                                      +7
                                      PCRE инструмент разработки =) дрочу я на телок =)
                                      –1
                                      заменить PCRE на assembler или "вспахивание поля лопатой" - ничего не изменится.
                                  –5
                                  Естественно угадывает! А как же иначе.

                                  Я даже не знаю что вам ответить. Бессмысленность вашего комментария поразила.
                                    0
                                    прежде чем получить доступ к ДОМу надо его свалидировать и распарсить.
                                      –1
                                      Спасибо что открыли мне глаза. Я даже и подумать не мог что это так. Я думал что превращение HTML в DOM это какое-то мистическое таинство... Теперь я знаю как это делается
                                  0
                                  Красивое.
                                  Но иногда отсутствие уязвимостей не так очевидно, каким может показаться. Неужели такое решение никак не обойти и не сломать? :)
                                    0
                                    Я и хочу попытаться выяснить это вместе с сообществом.
                                    Ни в коей мере не претендую на его пуленепробиваемость.
                                    0
                                    Абсолютно авторитетно автору темы хочу заявить, что его метод работать НЕ будет.

                                    Проблема в том, что настоящий парсер HTML-кода должен уметь парсить НЕ правильный html-код, т.е. такой код, где есть ошибки... DOM не сможет распарсить XML с ошибками и поэтому он совершенно не поможет, а вот любой браузер HTML-код с ошибкой все же выведет.

                                    В результате использования DOM-модели при проверке тегов у злоумышленника откроются огромные возможности по "запудриванию" HTML-кода с целью вставки туда вредоносного кода.

                                    На языке Perl есть модуль HTML::TreeBuilder, который отлично парсит любой HTML-код (даже с ошибками). На PHP подобного модуля я так и не нашел, хотя много раз искал.

                                    Др. метод - если у тебя Windows-сервер - использовать DOM-модель браузера Explorer, тогда точно правильно HTML-код распарсишь.

                                    P.S. Несколько строчек PHP-кода с регулярными выражениями так же совершенно не помогут правильно распарсить неправильный HTML-код. Это задача доволно сложная.
                                      0
                                      Абсолютно авторитетно заявляю, что этот код 100% рабочий и проверенный лично. Как вы совершенно правильно сказали XML с ошибками не парсится, а HTML - да. О чем я и написал в комментариях.

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

                                        Я написал свой коммент не для того, чтобы вас унизить и т.п., а лишь только для того, чтобы предостеречь вас от проблем. Но поступайте как хотите.
                                          0
                                          :) вы не совсем видимо понимаете суть. Суть в том что я получаю в PHP DOMDocument и САМ удаляю из нее те теги и аттрибуты, которые САМ считаю опасными. Только делаю это не регулярками и проходом по дереву DOM и его методами removeChild и removeAttribute. Потом сохраняю готовый результат как XML, этим самым добиваясь валидной верстки и кладу его в кеш-таблицу.
                                            0
                                            Я вас понимаю, и с DOM на PHP я работаю, имею представление об этой технологии. Больше не буду развивать свою мысль, но уверен, что в процессе эксплуатации данной схемы вы столкнетесь с проблемами, хотя желаю вам только успехов.

                                            В данный момент я тоже работаю над социальным проектом, где есть пользовательский ввод. В результате анализа вашей проблемы (у меня аналогичная проблема была) я все же пока отказался от разрешения html-тегов в пользовательских сообщениях и буду использовать только bb-теги. Конечно, это существенно ограничивает свободу пользователей, зато на 100% решает проблемы с безопасностью.
                                              0
                                              А вариант такой не подходит?:

                                              пользователю разрешается вставлять все html тэги — ничего не вырезается.
                                              в момент парсинга текста от пользователя все <, >, & заменяются на их мнемокод (lt, gt, quot), а потом с помощью регулярных выражение в стиле /<b>/i заменяем данный текст на и так далее. Только что самому в голову в результате чтения ваших комментариев — о бажности судить не могу =)
                                                0
                                                * пока плохо дружу с парсером хабра. :\

                                                … /& lt; b& gt;/i заменяем данный текст на <b> и так далее.
                                              0
                                              Кстати, к вопросу о removeAttribute: у DOM-расширения PHP5 с этим тоже, к сожалению, туговато. Мне удалось гарантированно удалять атрибуты только путём создания нового такого же элемента с последующим копированием в него из исходного элемента только разрешённых атрибутов.
                                                0
                                                У меня пока с removeAttribute проблем нет. Но спасибо за предупреждение. А вы не можете вспомнить, это касалось каких-то определенных аттрибутов?
                                        +1
                                        Ну а как к примеру можно избавиться от таких ситуаций:
                                        user text here
                                        Как убрать лушние элементы? Регулярками такое будет сложно сделать.
                                          0
                                          Вероятно в оригинале был HTML-код, который хабр пострипал.
                                            +1
                                            Сори, хабр съел теги =( Так вот:
                                            <span style="..."><span style="..."><span style="...">user text here</span></span></span>
                                              0
                                              а какие из них лишние?
                                              что сделать надо?
                                              а. убрать все span (тривиально)
                                              b. слепить все span stylе в один тег span, где style будет из все спанов.
                                              c. убрать конкретный span
                                                0
                                                а зачем их убирать?
                                                  0
                                                  А зачем захламлять контент лишними тегами?
                                                    0
                                                    А зачем допускать захламление на стороне клиента?
                                                  0
                                                  b. слепить все span stylе в один тег span, где style будет из все спанов.
                                                    0
                                                    Я бы не пропустил style от пользователя, так как там черти-что можно написать.
                                                0
                                                это неудобный вопрос для стороннков регэкспов, да ;)
                                                От себя добавлю лишь, что более-менее сложные операции над HTML/XML производить без DOM — это рехнуться можно. К примеру, вот такое преобразование
                                                +5
                                                я искренне считаю что люди, использующие @ в php должны умирать самой жестокой смертью, которая только может быть.
                                                  0
                                                  Думаю, здесь собака оправдана.
                                                    0
                                                    @ не оправдана. Лучше отловить ошибку try catch и обработать html другим способом :)
                                                      0
                                                      не забываем, что try catch начинается с php5
                                                        +1
                                                        Расширение DOM (не путаем с DOMXML) — тоже.
                                                        +1
                                                        Да и try-catch здесь тоже не оправдан, потому как ошибка пользователя при вводе данных - это вообщем то не исключение какое-то, а обычная ситуация, при том что данные обрабатываются DOMDocument-ом обработаются коррекно, вообще левые теги - вырежутся автоматом, недостающие - допишет сам ДОМ.

                                                        Вообще сам метод такой обработки спорный, и при его использовании следует поймать ответ от loadHTML-я (а он вернет false, если данные пришли хреновые) и решить следует ли продолжать, а его ворнинг просто напросто поймать обработчиком ошибок, как и все остальные ворнинги от пхпшных функций и поместить её в лог например.
                                                          +2
                                                          try...catch ловит exception'ы, а мы имеем дело с обычным ворнингом. Я бы в данном случае пожертвовал своими религиозными убеждениями и сделал так, как удобно, хоть и против религии. А собака тут удобна. Альтернатива - подготовить валидный XML средствами tidy или другой либы, но это ведь геммор, да и потом, не факт что они окажутся на сервере. DOM куда универсальнее.
                                                            0
                                                            Вот! Вы меня правильно поняли. Нет никакого смысла видеть эти ошибки в эррор логе. Там должны быть важные сообщения, а не "Invalid or misplaced tag in DOMDocument...". И пожертовать тактами на собачку выгоднее, чем запускать tidy или делать перехват вывода ошибок.
                                                              0
                                                              Все время забываю, что в комментах парсятся теги... :))
                                                        –1
                                                        Вот те кто х**ню в ЖЖ пишут должны «выхватить ебвальником» и умереть самой жестокой смертью.
                                                        А не х**ню писать.
                                                        –1
                                                        Недавно наткнулся на отечественную разработку, которая решает похожую задачу - http://jevix.ru/project/
                                                          0
                                                          SafeHTML тоже отечественный :)
                                                            0
                                                            про это недавно на хабре писали
                                                            +1
                                                            Вместо удаления ненужных тегов и атрибутов не лучше ли описать нужные теги и нужные у них атрибуты, после чего оставить только их? Тогда уж точно черный властелин не прокрадется через забытое опасное.
                                                              +1
                                                              Whitelist валидация однозначно лучше. Blacklist давно уже себя дискредитировала.
                                                                0
                                                                Согласен
                                                                0
                                                                зачем писать 5 строк если достаточно preg_replace('|]+>|','',$text) ?
                                                                  0
                                                                  preg_replace('|< [^> ]+>|','',$text)
                                                                    0
                                                                    это если все вырезать надо, а если все кроме тех, которые в Whitelist?
                                                                      0
                                                                      мда?! а если текст будет:

                                                                      ... а приведенном примере если a<b, то ... а если a>b, то ...

                                                                      потеряем часть текста - не страшно?
                                                                        0
                                                                        Хороший вопрос!
                                                                        интересно. буду думать.
                                                                          0
                                                                          Вы меня запутали. Из WYSIWYG ввод должен придти в виде ... а приведенном примере если a<b, то ... а если a>b, то ... :)
                                                                            0
                                                                            ... а приведенном примере если a&lt;b, то ... а если a&gt;b, то ...
                                                                              0
                                                                              1. Совсем не обязательно, все зависит от редактора. В большинстве есть редактирование HTML, которое можно сохранить (отправить вашем скрипту).
                                                                              2. Редактор может прислать любой код по сути. Если вы не словили частности, это не значит что ваш RegExp будет работать всегда - в таком виде как у вас, точно не будет. Можно придумать много примеров, я вам показал самый простой.
                                                                              3. Ваша тема "удаление ненужных HTML тегов из пользовательского ввода" а не "удаление ненужных HTML тегов из HTML текста переданного от WYSIWYG редактора".
                                                                          0
                                                                          Бред полный. Это то же самое , что сказать:
                                                                          "отправляйте почту с помошью метода sendEmail(), а реализацию этого метода я оставляю на ваше усмотрение."

                                                                          Автор, предложите свое решение проблемы. Или хотя бы потрудитесь сделать обзор уже существующих.
                                                                            0
                                                                            Какой проблемы? cleanNode делается под конкретную задачу. Откуда я знаю как и какие теги вы хотите убрать из дерева? Или какие атрибуты вам нужны, а какие нет? Или вы не умеете DOMDocumentом пользоваться? Так ведь пост-то не об этом.

                                                                          Only users with full accounts can post comments. Log in, please.