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. Исправленный код будет выглядеть так: Листинг : Таким образом в базе будет храниться информация с экранированными спецсимволами. После извлечения данных из БД обычно применяется обратная функция [Ссылка доступна только зарегистрированным пользователям[/url] для удаления экранирующих слешей. Ядовитая плюшка Кукис... как много в этом слове Чаще всего кукисы используются для хранения информации об авторизации или текущих настройках сайта. Очень странно, но многие, заботясь об известных уяз