31.4. PHP: Безопасность
Итак, мы умеем передавать идентификатор от одной страницы (РНР-скрипта) к другой (до следующего вызова с нашего сайта), а значит, мы можем различать всех посетителей сайта. Идентификатор сессии — это очень большое число (128 битов) и шансов, что его удастся подобрать перебором, практически нет. Поэтому злоумышленнику остаются следующие возможности:
• на компьютере пользователя устанавливают вирус типа «троянский конь», который ворует номера сессий;
• отлавливается трафик между компьютером пользователя и сервером. Конечно, есть защищенный (зашифрованный) протокол SSL, но им пользуются не все;
• незаметно подойти к компьютеру администратора и зафиксировать номер сессии.
Такие ситуации, основанные на том, что кто-то что-то у кого-то украдет, не входят в компетенцию программиста. Об этом должны заботиться администраторы и сами пользователи.
Впрочем, РНР очень часто можно «обмануть». Рассмотрим возможные точки взлома в программе авторизации пользователя.
Файл authorize.php — попытка подбора пароля с помощью скрипта.
Файл secretplace.php — попытка «обмануть» программу путем вписывания значений переменной $logged_user в адресной строке браузера, например, так:
http://www.yoursite.ru/secretplace.php?logged_user=my_host
Итак, в нашей программе явно видны две «дыры». Одна маленькая и не особо заметная, а вот вторая — просто огромная, через которую большинство хакеров и делает то, что не надо.
Как устранить «дыру» номер 1? Не будем писать тонны кода по блокировке IP-адреса и т. п., а просто проверим, откуда приходит запрос, а точнее, с какой страницы. Если это будет любая страница нашего сайта, то все нормально, а во всех остальных случаях пускать не будем. Подкорректируем файл authorize.php примера 31,6:
<?php
// открываем сессию
session_start();
// полный путь к корневой директории,
// в которой где расположены скрипты
$SERVER_ROOT = "http://localhost/test1/";
// если пользователь пришел с любой страницы нашего сайта, // то он наш.
// Переменная $HTTP_REFERER всегда доступна по умолчанию
// и содержит полный адрес ссылающейся страницы
// функция eregi() проверяет, начинается ли адрес ссылающейся
// страницы со значения в переменной $SERVER_ROOT
if (eregi("^$SERVER_ROOT", $HTTP_REFERER) ) {
// были ли данные отправлены формой
if($Submit) {
// далее все, как раньше
if ( ($user__name=="cleo" && ($user_pass=="password")) {
$logged_user = $user_name;
// запоминаем имя пользователя
session_register("logged_user");
// и переправляем его на "секретную" страницу
header ("Location: secretplace.php");
exit;
}
}
}
?>
<html>
<body>
Вы ввели неверный пароль!
</body>
</html>
Как избавиться от «дыры» номер 2? Предположим, у вас есть сайт, где каждый может зарегистрироваться, чтобы добавлять сообщения в форум. Естественно, в форуме некоторые пользователи (например, администраторы, модераторы) имеют больше возможностей, чем другие. Они, например, могут удалять сообщения других пользователей. Уровень доступа пользователя хранится в сессии, в переменной
$user_status, где $user_status = 10 соответствует полному доступу к системе. Пришедшему на сайт злоумышленнику достаточно зарегистрироваться обычным образом, а потом дописать в адресной строке браузера ?user_status=10. Вот и появился у вас на форуме новый администратор.
В принципе, любую переменную скрипта можно задать через адресную строку, просто дописав после полного адреса к скрипту вопросительный знак и название переменной с ее значением. Поправим код примера 31.7, чтобы избежать этой «дыры»:
<?php
// убираем все лишнее из адресной строки
// функция unset () "освобождает" переменную
unset($logged_user);
// открываем сессию
session_start();
// и корректируем "испорченные" переменные.
// Внимание: в этом случае переменная регистрируется не как
// новая, а как уже существующая, потому знак $ не опускается
session_register($logged_user);
if(!isset($logged_user)) {
header ("Location:index.php");
exit;
}
?>
<html>
<body>
Здравствуйте, <?php echo $logged user; ?>, вы на секретной странице!
</body>
</html>