Бесплатно PHP. Основы безопасного кода

Тема в разделе "Сборник статей по безопасности и анонимности", создана пользователем Hermanicus, 1 апр 2017.

  1. PHP. Основы безопасного кода

    В этой статье мы рассмотрим основные ошибки программиста, которые могут привести к "взлому" сайта.

    Инициализируй переменные!

    Пожалуй первое, что необходимо взять за правило, это инициализация переменных.

    PHP позволяет использовать переменные без объявления и инициализации, что в свою очередь упрощает процесс создания скриптов для начинающих программистов. Однако, это зачастую приводит к ошибкам и нежелательным последствиям.

    Давайте рассмотрим следующий пример.

    Есть переменная $Buffer, в которую складывается содержание массива с новостями ($NewsArr)

    Листинг : Возможность изменить переменную извне

    $NewsArr[] = 'Новость 1';

    $NewsArr[] = 'Новость 2'; foreach ($NewsArr as $News):

    $Buffer .= $News.'<br />'; endforeach; echo $Buffer;
    Нажмите, чтобы раскрыть...​
    Переменная $Buffer не инициализирована, поэтому мы можем положить в неё свои данные просто передав их в запросе к скрипту :)

    Необходимое условие для выполнения - REGISTER_GLOBALS должен быть включен.

    В PHP версией ниже 5, он включен по умолчанию и редкий хостер его выключает.

    В пятой версии register_globals по умолчанию выключен, однако проводить инициализацию нужно.

    Чтобы устранить уязвимость, да и повысить надежность кода, нужно инициализировать переменные самостоятельно.

    Листинг : Исправленный код

    $NewsArr[] = 'Новость 1';

    $NewsArr[] = 'Новость 2';

    $Buffer = ''; // - Инициализируем переменную Buffer foreach ($NewsArr as $News):

    $Buffer .= $News.'<br />'; endforeach; echo $Buffer;
    Нажмите, чтобы раскрыть...​
    Мой скрипт на вашем сайте

    Теперь переходим к самой опасной уязвимости PHP скриптов, заключающейся во вставке произвольных файлов в скрипт

    Атака с применением этой уязвимости называется PHP Source Injection.

    Рассмотрим на примере динамического сайта (иначе зачем нам нужен PHP?).

    В корне сайта есть папка pages в которой находятся .txt файлы с содержимым страницы.

    Для перехода на страницу новостей нужно перейти по следующему URL:

    Нажмите, чтобы раскрыть...​
    При этом за вывод страниц отвечает например такой кусок кода:

    Листинг :
    Код вывода страниц include './pages/'.$_GET['page'].'.txt';
    Нажмите, чтобы раскрыть...​
    Вроде бы всё просто, логично и безопасно, страницы лежат в папке , имеют расширение .txt, а значит не могут исполняться, да плюс ещё расширение приписывается скиптом.

    Однако тут есть один хитрый момент...


    Нажмите, чтобы раскрыть...​
    Такой запрос вставит в страницу и выполнит файл rss.php лежащий в за пределами папки pages!

    Всё дело в нулевом байте в конце запроса. Нулевой байт означает конец строки. Таким образом расширение .txt в скрипте уже не добавится.

    О том что путь "./pages/../" это то-же самое что и "./" думаю говорить не нужно, это и так ясно.

    Таким образом в скрипт можно вставить абсолютно любой файл.

    Выходов из ситуации может быть несколько.

    Первый это использовать жесткую логику на switch

    Листинг :
    Переключение страниц на Switch switch($_GET['page']): case 'index':

    $IncPage = 'index'; break; case 'news':

    $IncPage = 'news'; break;

    Default: // - Если в запросе "левая" страница

    $IncPage = 'index'; break; endswitch; include './pages/'.$IncPage.'.txt';
    Нажмите, чтобы раскрыть...​
    Однако жесткое переключение может быть неудобным или даже неприемлемым в некоторых ситуациях.

    В таком случае нужно фильтровать ввод:

    Листинг :
    Фильтрация ввода

    $IncPage = $_GET['page'];

    $IncPage = str_replace('/', '', $IncPage); // - Удаляемвсеслэши

    $IncPage = str_replace('', '', $IncPage); // - Удаляемвсеслэши

    $IncPage = str_replace('.', '', $IncPage); // - Удаляемвсеточки if(file_exists('./pages/'.$IncPage.'.txt')): // - Проверяемсуществутлитакойфайл

    // - Вставляем запрашиваемую страницу include './pages/'.$IncPage.'.txt'; else:

    // - Запрашиваемая страница не существует, вставляем главную include './pages/index.txt'; endif;
    Нажмите, чтобы раскрыть...​
    В зависимости от потребностей, алгоритм фильтрации может существенно меняться, но смысл должен остаться один - запретить вставку "чужого" файла.

    Также желательно отлавливать ошибки (например файл не существует) и выдавать на них либо "дефолтную" страницу, либо сообщение о попытке взлома или отсутствия страницы :)

    Сам себе дизайнер

    Рассмотрим очень частую уязвимость среди начинающих (и не только) программистов.

    Заключается она в недостаточном фильтровании спецсимволов HTML. Обычно приводит к внедрению произвольного HTML, JS и т.п. кода в страницу.

    Эта уязвимость также известна как "Межсайтовый скпритинг" (Cross-Site Scripting -> XSS).

    Посмотрим как выглядит уязвимый код:

    Листинг : XSS уязвимость

    <div><?=$Comment; ?></div>
    Нажмите, чтобы раскрыть...​
    Для программиста здесь просто выводится текст комментария, однако для хакера всё выглядит намного полезней :)

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

    Исправляется уязвимость очень легко: нужно заменить все опасные спецсимволы на их коды.

    Всё это делает функция htmlspecialchars.

    таким образом исправленный вариант будет выглядеть так:

    Листинг : XSS уязвимость исправлена

    <div><?=htmlspecialchars($Comment); ?></div>
    Нажмите, чтобы раскрыть...​
    Вообще стоит всегда фильтровать контент перед выводом. XSS уязвимости очень распространены и встречаются даже в очень крупных и известных продуктах.

    База данных под контролем

    Вот добрались и до базы данных. Трудно сейчас представить крупный сайт, не использующий БД для хранения практически всей (а зачастую всей) информации.

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

    Рассмотрим пример уязвимого запроса к MySQL базе данных.

    Есть стандартная форма входа на сайт и скрипт, обрабатывающий её:

    Листинг :
    Уязвимость SQL Injection

    $login = $_POST['login'];

    $User = mysql_query(" passwd users login='$login'";, $link);
    Нажмите, чтобы раскрыть...​
    Такой запрос имеет уязвимость SQL Injection, то есть взломщик может выполнить произвольные SQL команды.

    А при разрешении селекта в файлы это может привести к "взлому" всего сайта или даже целого сервера.

    И снова всё дело в спецсимволах... :)

    До выполнения запроса, их нужно экранировать!

    Это делается функцией addslashes.

    Исправленный код будет выглядеть так:

    Листинг :
    Уязвимость SQL Injection

    $login = addslashes($_POST['login']); // - Экранируем спецсимволы

    $User = mysql_query(" passwd users login='$login'";, $link);
    Нажмите, чтобы раскрыть...​
    Таким образом в базе будет храниться информация с экранированными спецсимволами. После извлечения данных из БД обычно применяется обратная функция [[/url] для удаления экранирующих слешей.

    Ядовитая плюшка

    Кукис... как много в этом слове :)

    Чаще всего кукисы используются для хранения информации об авторизации или текущих настройках сайта.

    Очень странно, но многие, заботясь об "известных" уязвимостях, наивно полагают, что куки совершенно безопасны.

    И правда, куки ставятся на стороне сервера, но хранятся то они у клиента... а всё, к чему имеет доступ клиент, может быть подделано!

    Через кукис можно использовать любую из описанных выше уязвимостей. Поэтому к данным, получаемым из Cookie, нужно относиться с таким же недоверием, как и к любым другим данным, получаемым от пользователя!
     

Поделиться этой страницей