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

CLAY
Оффлайн
Регистрация
25.01.17
Сообщения
763
Реакции
225
Репутация
292
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, нужно относиться с таким же недоверием, как и к любым другим данным, получаемым от пользователя!
 
Сверху Снизу