|
В разнообразных конференциях, посвященных программированию меня в
первую очередь всегда интересуют такие разделы, как "Web-программирование"
и "Скрипты". По большей части, вопросы о PHP в таких форумах довольно
простые, требующие лишь общего понимания PHP, тем не менее, самый часто
задаваемый вопрос по моим наблюдениям, это: "Что такое сессии в PHP и
с чем/как их можно кушать?". Хотелось бы разъяснить этот вопрос раз и
навсегда.
С самого начала PHP все приняли на ура, но как
только на этом языке стали создавать достаточно крупные проекты,
разработчики столкнулись с новой проблемой - в PHP отсутствовало понятие
глобальных переменных! То есть, выполнялся некий скрипт, посылал
сгенерированную страницу клиенту, и все ресурсы, используемые этим
скриптом уничтожались. Попробую проиллюстрировать: предположим есть две
страницы одного сайта, index.php и dothings.php. Исходники к этим
страницам выглядят так:
- index.php -
<?php $a = "Меня задали на
index.php"; ?> <html><body> <?php echo $a; ?>
</body></html>
- dothings.php -
<html><body> <?php
echo $a; ?> </body></html>
Если выполнить эти два скрипта, то на первой странице мы
увидим надпись "Меня задали на index.php", а вторая страница будет пустой.
Разработчики web-сайтов, недолго думая, стали использовать cookie
для хранения глобальных переменных на стороне клиента. Процесс выглядел
примерно так: пользователь приходит на главную страницу сайта, делает
какие-то действия, и вся информация, связанная с этим пользователем,
которая может потребоваться на других страницах сайта, будет храниться у
него в браузере в виде cookie. Этот метод имеет довольно серьезные минусы,
из-за которых от PHP в своё время отвернулось немало разработчиков.
Например, нам нужно авторизовать пользователя, чтобы разрешить ему доступ
к закрытым (или принадлежащим только ему) разделам сайта. Придёться
"кидать" пользователю cookie, который будет служит его последующим
идентификатором на сайте. Такой подход становится очень громоздким и не
удобным, как только сайт начинает собирать всё больше и больше сведений о
поведении пользователя, ведь всю информацию, посылаемую пользователю,
желательно кодировать, чтобы её нельзя было подделать. Ещё совсем недавно
подделкой cookie можно было "повалить" не один чат, а порой и пробраться в
чужую почту. К тому же есть ещё на свете странные люди, у которых браузер
cookie не поддерживает.
При использовании сессий вся информация
хранится не на стороне клиента, а на стороне сервера, и потому лучше
защищена от манипуляций злоумышленников. Да и работать с сессиями куда
проще и удобнее, так как все данные автоматически проходят через алгоритмы
криптографии модуля PHP. В броузере клиента, лишь хранится уникальный
идентификатор номера сессии, либо в форме cookie, либо в виде переменной в
адресной строке броузера, какой из двух способов использовать для передачи
идентификатора сессии между страницами интерпретатор PHP выбирает сам. Это
на 100% безопасно, так как идентификатор сессии уникален, и подделать его
практически не возможно (об этом чуть далее, в разделе о безопасности
сессий).
Я не буду вдаваться в технологические вопросы устройства
механизма работы сессий, а только опишу, как правильно работать с сессиями
в PHP.
|
Как работать с сессиями? Если вы будете
тестировать примеры из статьи (или ваши скрипты) session_start();
// задаём значение
переменной $a = "Меня задали на
index.php";
// регистрируем переменную с открытой сессией
// важно: названия переменных передаются функции
session_register() // без знака $
session_register("a");
?> <html>
<body> Всё ОК. Сессию
загрузили! Пройдём, посмотрим что <a
href="dothings.php>там…</a> </body>
</html>
- dothings.php - <?php //
открываем сессию session_start(); ?> <html> <body>
<?php
echo $a; ?>
</body> </html>
При
запуске этих файлов (в логической последовательности конечно), первый
скрипт (index.php) выдаст следующий результат:
Всё ОК. Сессию загрузили! Пройдём, посмотрим что там…
А второй (dothings.php) вот это:
Меня задали на index.php
Переменная $a
теперь доступна на всех страницах данного сайта, которые запустили сессии.
Другие полезные функции для работы с сессиями:
session_unregister(string) - сессия "забывает" значение
заданной глобальной переменной;
session_destroy() - сессия уничтожается (например, если
пользователь покинул систему, нажав кнопку "выход");
session_set_cookie_params(int lifetime [, string path [, string
domain]]) - с помощью этой функции можно установить, как долго будет
"жить" сессия, задав unix_timestamp определяющий время "смерти" сессии. По
умолчанию, сессия "живёт" до тех пор, пока клиент не закроет окно
браузера.
|
Примеры Теперь обратимся к практическому
применению механизма сессий. Давайте рассмотрим пару довольно простых и в
то же время полезных примеров.
Авторизация Пользователя
Вопросы по авторизации пользователей с помощью PHP-сессий
постоянно задаются в конференциях по web-программированию. Механизм
авторизации пользователей в системе с помощью сессий довольно хорош с
точки зрения безопасности (см. раздел "Безопасность" ниже). Наш пример
будет состоять из трёх файлов: index.php, authorize.php и secretplace.php.
Файл index.php содержит форму, где пользователь введёт свой логин и
пароль. Эта форма передаст данные файлу authorize.php, который в случае
успешной авторизации допустит пользователя к файлу secretplace.php, а
в противном случае выдаст сообщение об ошибке. Приступим:
- index.php - <html>
<head>
<title>Введи пароль,
смертный</title> </head>
<body> <form
action="authorize.php" method="post">
Логин:<input type="text"
name="user_name"><br>
Пароль:<input type="password"
name="user_pass"><br>
<input type="submit"
name="Submit"> color=#007700>("Location: secretplace.php");
exit;
} }
// если что-то было не так, то пользователь получит
сообщение об ошибке. ?>
<html><body> Вы ввели неверный пароль!
</body></html>
- secretplace.php -
<?php // открываем сессию session_start();
/*
просто зайти на эту страницу нельзя... если
имя пользователя не зарегистрировано, то
перенаправляем его на страницу index.php
для ввода логина и пароля... тут на самом деле
можно много чего сделать, например запомнить
IP пользователя, и после третьей попытки
получить доступ к файлам, его закрыть.
*/ if(!isset($logged_user)){
header("Location:
index.php"); exit;
} ?>
<html> <body>
Привет, <?php echo $logged_user; ?>, ты на секретной странице!!! :)
</body> </html>
|
Безопасность Итак, мы умеем передавать
идентификатор от одной страницы (PHP-скрипта) к другой (до следующего
вызова с нашего сайта), а значит мы можем различать всех посетителей
сайта. Так как идентификатор сессии - это очень большое число (128 бит),
шансов, что его удастся подобрать перебором, практически нет. Поэтому
злоумышленнику остаются следующие возможности:
на компьютере пользователя стоит "троян", который ворует номера
сессий;
злоумышленник отлавливает трафик между компьютером пользователя и
сервером. Конечно, есть защищенный (зашифрованный) протокол SSL, но им
пользуются не все;
к компьютеру нашего пользователя подошел сосед и стащил номер сессии.
Такие ситуации, основанные на том, что кто-то что-то у кого-то стащит,
в общем, не входят в компетенцию программиста. Об этом должны заботиться
администраторы и сами пользователи.
Впрочем, PHP очень часто можно
"обмануть". Давайте рассмотрим возможные точки взлома в программе
авторизации пользователя:
Файл authorize.php - попытка подбора пароля с помощью стороннего
скрипта;
Файл secretplace.php - попытка обмануть программу путём вписывания
значений переменной $logged_user в адресной строке браузера, например так:
http://www.yoursite.ru/secretplace.php?logged_user=hacker
Итак, в
нашей программе явно видны две "дыры", одна маленькая и не особо заметная,
а вот вторая - просто огромная, через которую большинство хакеров и лезет
туда, куда не надо.
Как "залатать" дыру номер 1?
Не будем писать тонны кода по блокировке IP-адреса и т.п., а
просто проверим, откуда приходит запрос, а точнее с какой страницы пришёл
запрос, если это будет любая страница с нашего сайта, то всё нормально, а
во всех остальных случаях пускать не будем. Подкорректируем файл
authorize.php:
- authorize.php V2 - <?php //
открываем сессию = $user_name;
// запоминаем имя пользователя
session_register("logged_user");
// и переправляем его на "секретную" страницу...
header("Location: secretplace.php");
exit;
} }
} ?>
<html><body> Вы ввели неверный пароль!
</body></html>
Как избавиться от "дыры"
номер 2? Предположим, у вас есть сайт, где каждый смертный может
зарегистрироваться чтобы добавлять сообщения в форум. Естественно, в
форуме у некоторых пользователей (админов, модераторов), возможностей
больше чем у других, они, например, могут удалять сообщения других
пользователей. Уровень доступа пользователя вы храните в сессии, в
переменной $user_status, где $user_status = 10 соответствует полному
доступу к системе. Пришедшему на сайт злоумышленнику достаточно
зарегистрироваться штатным образом, а потом дописать в адресной строке
браузера ?user_status=10. Вот и завёлся у вас на форуме новый админ!
В принципе, любую переменную скрипта можно задать через адресную
строку, просто дописав после полного адреса к скрипту вопросительный знак
и название переменной с её значением. Давайте поправим наш код, чтобы
этого избежать:
- secretplace.php V2 - <?php //
убираем всё лишнее из адресной строки // функция unset()
"освобождает" переменную unset($logged_user);
//
открываем сессию session_start();
// и корректируем
испорченные перменные. // Важно: в этом случае, переменная
регистрируется не как новая // переменная, а как уже
существующая, а потому знак $ не опускается session_register($logged_user);
/*
просто зайти на эту страницу нельзя... если
имя пользователя не зарегистрировано, то
перенаправляем его на страницу index.php
для ввода логина и пароля... тут на самом деле
можно много чего сделать, например запомнить
IP пользователя, и после третьей попытки
получить доступ к файлам, его перекрыть.
*/ if(!isset($logged_user)){
header("Location:
index.php"); exit; }
?> <html>
<body>
Привет, <?php echo $logged_user; ?>, ты на секретной странице!!! :
|