Скрыть объявление

Дорогой друг! Видимо, ты заинтересован зарабатывать в интернете. Поэтому задача нашего Форума не дать тебе подарить мошенникам твои деньги и научить грамотно ими распоряжаться.
Администрация Форума настоятельно рекомендует тебе начать знакомство с Форумом этой статьёй!
Не рискуй твоими деньгами понапрасну!

С Уважением, Администрация Форума MoneyMaker.team

Обзор PHP: примеры уязвимостей

Форум о Заработке и Инвестициях

Тема в разделе "Азы, уроки", создана пользователем rez, 22/9/19.

  1. rez

    rez Администратор Команда форума

    Регистрация:
    1/2/14
    Сообщения:
    11,349
    Симпатии:
    1,151
    Баллы:
    181
    Пол:
    Женский
    На днях, изучая некий скрипт, обнаружила очень интересную реализацию в плане академического примера того как не надо писать на PHP. Скрипт представляет из себя некий аналог модульной системы подключений без роутинга(?), а наш "герой дня" отдельным файлом инклюдится в корень до начала прочих подключений и всевозможных обработок, и имеет следующий код:
    PHP:
    function Optimize$s ) {
        
    $s preg_replace'/(\>)(\r\n)/''$1'$s );
        
    $s preg_replace'/\s{3,}/'' '$s );

        return 
    $s;
    }
    По замыслу программиста такая оптимизация будет применена ко всему коду. Что делает эта функция конкретно? Второй preg_replace, как мы видим, банально трёт слитные пробелы, если их больше 2-ух, всё это удовольствие превратится в единичный пробел, что уже ограничит простор для любителей похулиганить с входящими данными. Во второй части регулярки первого preg_replace не сработает попытка переноса строки (если кто-то попытается ввести такую радость в поле формы). А вот первый блок, конструкция "\>", выглядит весьма любопытным решением. Представим себе на минутку, как некий хакер, потирая руки, вводит в форму следующее:
    Код:
    <script>function zloyHacker() { alert("Привет! Я Злой Хакер и я тебя взломаю!!!");}</script>
    не получит никакого ответа. Потому что открывающий тег "<" пройдёт "оптимизацию" беспрепятственно, а вот закрывающий будет вырезан и сценарий не отработает. Как говорится, "всё гениальное просто"! Найден "Грааль", "совершенная" оптимизация!(?) Именно так считает программист. Но... так ли думает хакер? А хакер взглянет на кодировку (PHP и HTML в документе кодируются в WINDOWS-1251, что уже не есть хорошо, причём технически и её можно подделать, особенно в приведенной реализации, но об этом как-нибудь в другой раз) и банально заменит в предыдущем коде ">" на "&#62;" или даже на "&#x3E;", и поскольку у нашей "оптимизации" специализация узкая, этот код пройдёт, и атака состоится, а в зависимости от вектора, может затронуть как пользовательскую, так и серверную части, с самым произвольным результатом, теоретически, даже получением полного контроля над сервером (если, скажем, команду system не запретили, что в нашем примере маловероятно)! Плохо, правда?

    Но критиковать чужой труд все мастаки. Как же сделать правильно? С чего бы я начала работу над ошибками?

    1.Смена кодировки документа. В последние годы веб-разработка ведётся в общепризнанной единой международной кодировке UTF-8, и даже PHP, если не изменяет память, с версии 5.4 с ней подружили. Так что устанавливаем эту кодировку везде: для HTML, PHP и MySQL. В отличие от WINDOWS-1251 кодировка многобайтовая, так что в PHP-сценариях при необходимости будем использовать mb-функции.

    2.Подготовительная часть на этом закончена. Мы должны понимать, что невозможно создать единый алгоритм обработки и фильтрации абсолютно всех поступающих в сценарий данных. Лень в этом щепетильном вопросе неуместна! Необходимо проверять всё: данные из БД, файловой системы, переменных окружения, а особенно - от пользователя. На первой стадии мы должны запретить абсолютно всё и абсолютно всем. Далее начинаем разрешать использование только того, что приведёт к ожидаемому (нами) результату. Как пример, если у пользователя в куки ожидаются только латинские буквы нижнего регистра и цифры, причём количество символов строго ограничено, проверять их мы будем только на это соответствие:
    PHP:
    $_COOKIE{'autorize'} = preg_match('#^[a-z0-9]{20,30}+$#u'$_COOKIE{'autorize'}) ? 'Это Ваши куки, входите, пожалуйста!' 'Вы попытались подделать куки, Вам здесь не место!';
    Проверять надо каждый пункт, каждую мелочь. Например, если сценарий обязуется выдать по запросу страницу с новостью, то мы обязаны предусмотреть варианты не просто целого числа (номер страницы), но и отличного от 0, исключительно положительного и ограниченного по максимуму. Т. е. используем механизм проверки:
    PHP:
    $page filter_var($intFILTER_VALIDATE_INT, array("options" => array("min_range"=>1"max_range"=>999999))) ? 'Всё верно, выдаём страницу по запросу' 'Вы что-то там химичите, давайте-ка погуляйте...';
    Такой фильтр пропустит только целочисленные страницы, и только от 1 до 999999, во всех остальных случаях вернётся FALSE и сообщение отправит потенциального вредителя куда подальше. А что было бы, если бы мы разрешили просмотреть 0-ую страницу? Или -1-ую? Или не ограничили бы количество, а неправильные настройки или серверные ошибки привели бы к переполнению буфера и отказу в обслуживании? Скажите: никогда с таким не сталкивался и считаю такое невозможным. Ошибаетесь: ещё как возможно! А по закону Мёрфи в самый неподходящий момент отработает именно самый непредсказуемый сценарий. Электроника и серверное ПО и без того ненадёжны, так что давайте хоть мы будем делать всё грамотно!

    3.Ну а теперь сам пример. Вообще, существует верная практика: болезнь проще предупредить, чем лечить. Поэтому старайтесь в Ваших сценариях избегать использование что-либо "обрезающих" функций. Правильно выдать сообщение об ошибке, когда человек прописал недопустимый символ. Но мало кому понравится перекраивание его трудов; в следующий раз пользователь подумает, возвращаться ли ему на Ваш сайт. Но уж если продолжать по плохому...
    Давайте перепишем наш опытный образец с учётом всего вышеописанного. Вместо первого блока preg_replace используем мою любимую strtr. Почему она любимая? а)потребляет гораздо меньше серверных ресурсов, чем preg_replace, б)работает во много раз быстрее. А нам всего-то нужна замена символа (если по хорошему, 2-ух символов). Итак, получим следующее:
    PHP:
    <?php

    header
    ('Content-Type: text/html; charset=utf-8');

    $s strtr($s, array('<' => '''>' => '''&lt' => '''&lt' => '''&#60' => '''&#62' => '''&#x3C' => '''&#x3E' => ''));

    ?>
    Вот так уже получше. Здесь мы вырезали оба тега во всех его интерпретациях. Уже вставленная хакером команда
    Код:
    <script&#62;function zloyHacker() { alert("Привет! Я Злой Хакер и я тебя взломаю!!!");}</script&#62;
    не сработает. Правда, поле пропустит бессмысленные в данной ситуации символы ";", которые, к слову, означают конец строки в PHP, а значит, могут представлять угрозу! Так что раз уж вырезать, режем всё. Заодно и переносы строк с возвратом каретки. К слову, вариант разработчика пропускал html-символы перевода на следующую строку типа <br>, <p>, <h1...>, <div> и т. п., так что защита по PHP была однобокой. Наш вариант удалит все теги, потому перевести данные на новую строку уже не представится возможным:
    PHP:
    <?php

    header
    ('Content-Type: text/html; charset=utf-8');

    $s strtr($s, array('<' => '''>' => '''&lt' => '''&lt' => '''&#60' => '''&#62' => '''&#x3C' => '''&#x3E' => '''\r' => '''\n' => ''';' => ''));

    ?>
    Вот теперь точно всё, хакер не пройдёт!(???) Вы в этом уверены? А я уверена, что пройдёт! Просто конкантенацией заменит символ "&" в коде на "&amp;" (или на "&#38;", или на "&#x26;"), можно всё то же, только без ";". К слову, символ ";" может быть представлен и как "&#59;", и как "&#x3B;". А ещё символ решётки: "&#35;" и "&#x23;". А ещё... А может довольно заниматься глупостями? Даже если мы вырежем все потенциально опасные символы, мы не сможем быть уверенными, что взломщик не применит другую технику. Как Вам такое решение в PHP реализации:
    PHP:
    &l/**/t
    Будем продолжать возводить частокол от хакера на все оправданные и не оправданные случаи? Или... смиримся и... используем "белый список". Если в этом поле может использоваться точка, но данные записываются в файл, это уже не хорошо, но достаточно убедиться, что точка будет хотя бы одна (а не 2 подряд). Поэтому надо локализовать все комбинации и запретить использование многоточия в тексте. А чтобы не устраивать погоню за ветряными мельницами, гораздо уютнее загнать текст, даже со всеми точками, сколько их там, в базу и запретить его использование любым возможным путём в файловой системе. Поверьте, спать будете крепче!
    Ну а что касается игр хулиганов с пробелами, я действительно не возражаю против этого решения:
    PHP:
    $s preg_replace'/\s{3,}/'' '$s );
    Только для чего разрешать 2 пробела подряд? Так виднее, что ли? Современные браузеры их всё равно режут до одного: чай не 90-ые ныне...