Работая с массивами $_GET и $_POST очень важно не забывать обрабатывать значения. Иначе можно получить неприятный сюрприз в виде разных инъекций. Об этом написано уже много статей. Но тем не менее, все равно встречается код с прямым выводом информации на страницу (возможна XSS инъекция)

<?php
echo $_POST['name'];
?>

или запросом к базе данных (возможна SQL инъекция)

<?php
$name = $_GET['name'];
$query="SELECT * FROM table WHERE name like '$name'";

Такой сайт рано или поздно будет взломан. Чтобы этого избежать важно запомнить главное правило:

Никогда не доверяйте данным, которые приходят от пользователя!

Фильтрация данных

Для того, чтобы обезопасить сайт от различных инъекций, обязательно фильтруйте данные. Давайте рассмотрим несколько способов.

Для начала откажитесь от использования глобальной переменной $_REQUEST. Если вы работаете с данными формы, то обращайтесь к $_POST. Параметры запроса можно получить в $_GET переменной. Не упрощайте задачу злоумышленнику.

Цифровые данные приводите к соответствующему типу:

<?php
$page = (int) $_POST['page'];
echo 'Страница с номером' . $page;

Также фильтровать можно с помощью htmlspecialchars() и filter_input(). Они пригодятся, если нужно подготовить данные для вывода на страницу. А еще, чтобы вырезать все HTML и PHP теги, можно использовать функцию strip_tags().

Функция $modx->stripTags()

В MODX есть функция, которая расширяет php-шную strip_tags(). Она вырезает не только HTML и PHP теги, но MODX теги, а также позволяет дополнительно указать запрещённые паттерны (шаблоны regex).

<?php
public function stripTags($html, $allowed= '', $patterns= array(), $depth= 10) {
    $stripped= strip_tags($html, $allowed);
    if (is_array($patterns)) {
        if (empty($patterns)) {
            $patterns = $this->sanitizePatterns;
        }
        foreach ($patterns as $pattern) {
            $depth = ((integer) $depth ? (integer) $depth : 10);
            $iteration = 1;
            while ($iteration <= $depth && preg_match($pattern, $stripped)) {
                $stripped= preg_replace($pattern, '', $stripped);
                $iteration++;
            }
        }
    }
    return $stripped;
}

Как мы видим из определения функции, это третий аргумент. Если ничего не передать, что функция будет использовать шаблон по-умолчанию $modx->sanitizePatterns. Он представляет собой массив с паттернами для preg_replace():

array(
    'scripts'   => '@<script[^>]*?>.*?</script>@si',  // Вырезает тег script. 
    'entities'  => '@&#(\d+);@',    // вырезает все HTML сущности
    'tags1'     => '@\[\[(.*?)\]\]@si', // непустой тег MODX
    'tags2'     => '@(\[\[|\]\])@si',   // двойные скобки тегов MODX
);

У некоторых может возникнуть вопрос, зачем указывать тег script, если он и так вырезается strip_tags(). Это станет понятно дальше.

Функция $modx->sanitize()

Ещё MODX позволяет отфильтровать сразу массив.

$post = $modx->sanitize($_POST, $modx->sanitizePatterns);
// или так
$post = modX::sanitize($_POST, $modx->sanitizePatterns);

Но в этом методе не выполняется strip_tags(), а значит HTML и PHP теги не вырезаются. Вот тут как раз и пригодится шаблон в массиве $modx->sanitizePatterns для тега script — самого опасного тега для межсайтового скриптинга XSS. А если нужно вырезать теги HTML и PHP, то добавьте их в pattern по аналогии с тегом script.

Функция $modx->sanitizeString()

Еще есть одна убойная функция $modx->sanitizeString(). Она не только удаляет HTML и PHP теги, но удаляет почти все символы кроме разрешенных. Как следует из названия, её применяют к строкам (ведь цифровые данные мы фильтруем принудительным приведением к соответствующему типу).

public function sanitizeString($str,$chars = array('/',"'",'"','(',')',';','>','<'),$allowedTags = '') {
    $str = str_replace($chars,'',strip_tags($str,$allowedTags));
    return preg_replace("/[^A-Za-z0-9_\-\.\/\\p{L}[\p{L} _.-]/u",'',$str);
}

По-моему, эта функция несколько не доработана, так как второй аргумент, в котором указываются символы для удаления, вообще ни на что не влияет. Как собственно и третий. В функции preg_replace() указан свой набор разрешенных символов, который не зависит ни от аргумента $char, ни от $allowedTags. Так что, сохранить скобки при обработке телефона не получится, даже если в аргументе $char ничего не указать. Как и разрешенный HTML тег. Вот что я имел под словом «убойная».

Несмотря на то, что переменные $_COOKIE и $_SERVER не передаются ни через форму, ни через параметры запроса, их использование без обработки может быть небезопасным. Поэтому их также нужно фильтровать вышеописанными способами.

Защита от SQL-инъекций

Для защиты от этого вида угроз нужно использовать функцию mysql_real_escape_string() для экранирования специальных символов. Эта функция делает гораздо больше, чем устаревшие addslashes() и mysql_escape_string(). Во-первых, она облегчает ведение и чтение логов mysql, заменяя, например, символ перевода строки на "\n" и некоторые другие символы на escape-последовательности. Во-вторых, и самое главное — она корректно работает с многобайтными кодировками, принимая во внимание текущую кодировку MySQL и не портит, таким образом, тексты в кодировке Unicode.

С версии PHP 5.5.0 эта функция помечена как устаревшая и будет удалена в будущем. В качестве альтернативы используйте mysqli_real_escape_string() или PDO::quote().
// В $_GET['name'] лежит значение д'Артаньян (это самый безобидный пример)
// Экранируем
$name=mysql_real_escape_string($_GET['name']);
// Строим запрос, который будет работать без ошибок
$query="SELECT * FROM table WHERE name LIKE '$name'";

В MODX эту функцию заменяет $modx->quote(). Она добавляет кавычки (если нужно) и экранирует специальные символы. Т.е. тот же самый код можно переписать так

// Экранируем
$name=$modx->quote($_GET['name']);
// Переменную $name уже не надо оборачивать в кавычки
$query="SELECT * FROM table WHERE name LIKE $name";

Еще один способ обезопасить себя от подобной проблемы — подготавливать запрос к базе данных. И использовать для этого возможности PDO. В MODX есть удобное расширение для работы с PDO — xPDO. Я считаю данный способ лучшим вариантом при разработке в MODX. Вот некоторые примеры его использования — Про xPDO, Пара фокусов с xPDO, ряд статей по xPDO.

Заключение

Тема серьезная и требует внимательного изучения, если вы хотите повысить безопасность сайта.

23 декабря 2015, 17:50   2313     4

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

  1. cimazov 23 августа 2017, 16:38 # 0
    Доброго дня. Подскажите, при использовании XPDO, есть ли смысл в дополнительном экранировании?
    К примеру
    $raz= $_GET['raz'];
    $query = $modx->newObject('Object');
    $query->set('sender', $raz);
    $query->save();
    
    Благодарствую
    1. Сергей Шлоков 23 августа 2017, 17:25 # +1
      В данном случае может и не нужно. Но лучше взять за привычку обрабатывать пользовательские данные. Так например, год назад в Tickets была дыра, позволяющая злоумышленнику использовать sql injection. И xPDO никак не помог.
      // Так делать нельзя!!!
      $object = $modx->getObject('Object', $_GET['id']); 
      // Как минимум так
      $object = $modx->getObject('Object', (int) $_GET['id']);
      
      1. cimazov 23 августа 2017, 17:39 # 0
        Весьма признателен. Спасибо
        p.s. пару дней назад пытался оставить здесь данный вопрос с другого аккаунта (skarb), однако всплывала ошибка: недостаточно прав
        1. Сергей Шлоков 23 августа 2017, 18:53 # 0
          Видимо при регистрации произошел сбой, так как пользователь не попал в группу. Добавил вручную.

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

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