• Блог
  • Фильтрация входящих данных

В Москве прошёл митап, на котором Евгений Борисов осветил аспект безопасности MODX. Его доклад можно найти в сообществе. И вот, прочитав его, некоторые разработчики (в том числе и я), обнаружили, что чересчур доверяли MODX в плане фильтрации данных. Правда некоторые вообще ничего об этом не знают, но это их не оправдывает. В общем, одна из проблем фильтрации, которую он показал, работает на большинстве сайтов. А вот почему, давайте пробежимся.

Когда в обычном режиме поступает запрос, MODX запускает метод protect, который фильтрует некоторые данные запроса ($_SERVER["HTTP_USER_AGENT"], $_SERVER["HTTP_REFERER"], $_SERVER["QUERY_STRING"]). Дальше запрос передаётся классу modRequest, который фильтрует входящие данные в массивах $_GET, $_POST, $_REQUEST и $_COOKIE. Для фильтра он использует вот такой массив:

$sanitizePatterns = array(
    'scripts'  => '@<script[^>]*?>.*?</script>@si',
    'entities' => '@&#(\d+);@',
    'tags1'    => '@\[\[(.*?)\]\]@si',
    'tags2'    => '@(\[\[|\]\])@si',
);

Как видим, из запроса вырезаются тег script, коды символов и теги MODX. Т.е. если указать в запросе тег [[someTag]], то он в массиве данных будет удалён. Но Евгений показал, как этот фильтр можно легко обойти. Достаточно добавить теги HTML.

[<b></b>[someTag]<b></b>]

Такая строка под регулярку не попадает. И если эти данные выводятся на странице, то, как говорится, «Милости просим». И это как бы не косяк MODX. Хотя к настройке allow_tags_in_post можно было бы добавить настройку для вырезания тегов HTML из запросов. Но пока решение этой задачи нужно будет взять в свои руки. Для этого придётся создать плагин и в нём самостоятельно фильтровать данные.

Первое, что можно сделать — удалить теги HTML. В этом случае Женин пример уже работать не будет. MODX сам справится и удалит свои теги из запроса. Второй вариант — кодировать скобки как советовал Евгений. В этом случае MODX их, конечно, не вырежет, но и парсить не будет.

Лично я выбрал оба варианта. Для удобства я добавил пару функций в библиотеку modHelperstag_encode() и tag_decode(), которые преобразуют и квадратные и фигурные скобки в соответствующие коды. Вот как это выглядит:

class webListener extends Middlewares\Listener
{
    public function OnMODXInit($before=true)
    {
        // Фильтруем скобки
        foreach ($_REQUEST as $key => &$value) {
            if (is_string($value)) {
                $value = tag_encode(strip_tags($value));  // Новая функция modHelpers
            }
        }
        foreach ($_POST as $key => &$value) {
            if (is_string($value)) {
                $value = tag_encode(strip_tags($value)); // Новая функция modHelpers
            }
        }
        foreach ($_GET as $key => &$value) {
            if (is_string($value)) {
                $value = tag_encode(strip_tags($value)); // Новая функция modHelpers
            }
        }
        foreach ($_COOKIE as $key => &$value) {
            if (is_string($value)) {
                $value = tag_encode(strip_tags($value)); // Новая функция modHelpers
            }
        }
    }

    public function OnWebPagePrerender()
    {
        // Чтобы на странице выводились не коды, возвращаем скобки.
        $output = &$this->modx->resource->_output;
        $output = htmlspecialchars_decode($output);
    }
}

Как видите, я использую компонент Middlewares вместо плагинов, но вы можете сделать классический плагин на события с названиями методов. Только у события OnMODXInit поставьте отрицательный приоритет, чтобы выполнялся раньше других плагинов.

Что важно ещё отметить. Я в предыдущей статье призывал разработчиков отказаться от использования массива $_REQUEST. Но многие разработчики игнорируют данную рекомендацию, чем упрощают жизнь всяким хулиганам (ибо вместо POST запроса можно слать GET, что проще). И поэтому не забывайте фильтровать все массивы с данными. Давайте я даже это выделю.

Важно!

Кроме $_GET и $_POST массивов обязательно фильтруйте и $_REQUEST!

Заключение

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

И ещё нужно знать, что в API режиме (define('MODX_API_MODE', true)) обработчик запроса (класс modRequest) не запускается. Поэтому и описанной выше стандартной фильтрации MODX не будет.

П.С. Себе я новые функции modHelpers уже добавил, а пакет выпущу чуть позднее, так как нужно не только русскую документацию добавить, но и буржуйскую.

03 октября 2018, 09:03   615     3

Комментарии ()

  1. Сергей Шлоков 03 октября 2018, 17:52 # +1
    Женя посоветовал ещё и $_COOKIE фильтровать от греха подальше. Добавил в пример.
    1. Vladimir 04 декабря 2018, 12:05 # 0
      @telefeedbot сейчас а телеге (rss извещения) эту статью пометил обновленную.
      Не вижу, что-то дополнено?
      1. Сергей Шлоков 04 декабря 2018, 18:50 # 0
        Я тоже не вижу :)

        Видимо глюк рсс.

      Вы должны авторизоваться, чтобы оставлять комментарии.

      Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.