PHP – довольно простой скриптовый язык, не требующий серьёзных навыков программирования. Однако, часто при разработке сайтов начинающий веб-мастер может, сам того не подозревая, создать уязвимость сайта себе или своему клиенту. Порой, для решения какой-либо задачи веб-мастер делает поиск в Интернет, и найденное решение помещает к себе на сайт, не вдаваясь в детали, что же именно делает скопированный им кусок кода, открывая очередную «дыру» в безопасности. Несколько простых советов, приведённых ниже, помогут предотвратить появление серьезных уязвимостей Вашего сайта.
Стоит заметить, что простые принципы, описанные здесь, относятся не только к PHP, но также и к любому другому языку, на котором создаётся веб-приложение.
Веб-приложение, каковым являтся любой сайт, как правило, работает по следующему сценарию:
- получение запроса от пользователя или другого сайта по HTTP
- обработка запроса PHP-скриптом
- вывод результата запроса: генерация веб-страницы, HTML или другого кода (к примеру, RSS документа).
На каждом из этих этапов возможны ситуации, когда программа делает совсем не то, что хотелось веб-мастеру.
В этой статье я решил описать несколько самых часто встречающихся типов “граблей”, наступив на которые веб-мастер может серьезно нарушить безопасность сайта. Все ниженаписанное относится к этапу приёма пользовательского запроса веб-сайтом.
Содержимое параметров запроса
Протокол HTTP передаёт все параметры запросов в виде строк. В PHP значения параметров приходят как строки, даже если в скрипт ожидает число. Проблем с этим нет, строку «1» PHP с легкостью и незаметно преобразует в число 1, так что в PHP «1»+2 будет равно 3. Это очень удобно, но порой бывает опасно. Если полученное значение попадает напрямую в запрос к базе данных или используется для конструирования имени файла на хостинге, злоумышленник может составить такой запрос, который уничтожит данные в базе данных (SQL Injection) или осуществит доступ к закрытым файлам на хостинге.
$userId = $_GET[“uid”]; // А это грубое нарушение безопасности: $user = query_database(“SELECT * FROM users WHERE id=$userId”);
Заделывается такая «брешь» просто:
$userId = (int) $_GET[“uid”];
Теперь можно смело помещать это значение в SQL-запрос, никакого «мусора», приписанного клиентом там быть не может, теперь это просто число. Ещё можно проверить, не равно ли значение $userId нулю, чтобы не пытаться делать запрос в базу данных с заранее несуществующим ID.
В случае, если в SQL запрос необходимо поместить строку, принятую чрез HTTP-запрос, достаточно воспользоваться mysql_escape_string():
$name = $_GET[“name”]; $name = mysql_escape_string($name); $user = query_database(“SELECT * FROM users WHERE name='$name'”);
В этом случае все попытки сфабриковать “зловредный” SQL-запрос, передав в $name хитровыдуманное значение, провалятся.
Заливка файлов (upload)
Аналогичная ситуация возникает с загрузкой файлов через форму заливки. Информация о закачанных файлах появляется в PHP скрипте в переменной $_FILES, причём в $_FILES['имя_параметра']['name'] находится имя файла, под которым он лежал на машине клиента. Реальное же имя, под которым залитый файл оказался на сервере, будет совсем другим — это будет имя временного файла, сгенерированное PHP автоматически. Для удобста веб-мастер может переименовать этот временный файл, назвав его так, как он был назван на машине клиента, взяв это имя из $_FILES.
Тут и сидит очередная потенциальная «дыра» в безопасности. Значение $_FILES['имя_параметра']['name'] клиент (точнее, недоброжелатель) может передать на сервер произвольным, поэтому его необходимо тщательно обработать, прежде чем оно станет пригодно для использовния в качестве имени файла на сервере. Что надо проделать? Примерно следующее:
- Убрать путь к файлу, (все, что находится перед последним символом ‘/’ или ‘\’)
- Можно, но не обязательно, проделать транслитерацию, приведя строку с именем файла к латинице.
- Убрать все символы, которые нарушают читаемость или не поддерживаются в файловой системе сервера. Если точнее, оставить только символы из набора (A-Z, a-z, 0-9, ‘.’ точка, ‘_’ подчёркивание, ‘-’ минус). Этого должно быть достаточно.
- Обеспечить, чтобы первым символом имени файла не была точка (так обычно обозначаются «скрытые» файлы, но главное, что имя файла «.» или «..» скорее всего недопустимо, а порой и опасно).
- Можно, но не обязательно, проверить также расширение файла (текст, находящийся после последней точки). Тип содержимого файла так, конечно, не определить, но это можно сделать для обеспечения читаемости имени файла.
- Удостовериться, что длина получившегося имени находится в разумных пределах: от 1 до 50 символов, к примеру.
- Наконец, проверить, не совпадает ли полученное имя с уже существующим файлом, чтобы предотвратить «перетирание» важных данных.
Кажется, ничего не забыл, но этот список зависит от конкретной ситуации.
Неинициализированные переменные
PHP в принципе разрешает использование неинициализированных переменных. Правда, при этом он выдает предупреждение. Использование неинициализированных переменных — само по себе уже большое неряшество, но в совокупности с включенной опцией PHP register_globals это становится реально опасным. К счастью, опция register_globals отключена в PHP по умолчанию начиная с PHP 4.2.0. Эта опция автоматически регистрирует все параметры HTTP запроса и содержимое сессии в качестве переменных. То есть, если запрос имеет вид «http://site/script.php?x=y», то со включенным register_globals в скрипте автоматически будет присутствовать переменная $x со значением “y”. Если же скрипт использует переменную $x и не инициализирует ее, то возможна ситуация, когда клиент может повлиять на поведение скрипта, передав в запросе значение переменной, которое от клиента приниматься не должно. Как предотвратить такой тип уязвимости? Тоже просто: в php.ini должны быть следующие параметры:
- register_globals = Off (это значение по умолчанию)
- error_reporting = E_ALL (это нужно на машине разработчика, чтобы увидеть все предупреждения PHP)
- error_log=”путь к лог-файлу PHP” — поможет отловить ошибки и предупреждения из скриптов, которые не выдают явно веб-страниц (скрипты редиректа, итд)
С такими настройками PHP сразу сообщит, что используется неинициализированная переменаая, что надо сразу же исправлять.
Возможно, если в голову придёт еще что-то по данной теме, я допишу, но пока ничего не пришло ![]()
Спасибо за внимание.













Comments
Leave a comment Trackback