Лабораторная работа № 7.
Управление сессиями.

Цель работы: Получить практические навыки управления сессиями.

Базовые сведения:

Сессии в PHP

Сессии - это механизм, который позволит вам создавать и использовать переменные, сохраняющие свое значение в течение всего времени работы пользователя с вашим сайтом. При этом у каждого пользователя вашего сайта эти переменные будут собственными, т.е. их область видимости (variable scope) распространяется на все время нахождения на сайте конкретного пользователя, причем для каждого захода пользователя на ваш сайт эти переменные будут различными. Говоря проще, эти переменные принадлежат конкретной сессии работы конкретного пользователя с вашим сайтом (отсюда и название механизма - сессии).

Для того, чтобы понять всю важность этого механизма необходимо понимать отличие в принципах построения обычных программ, выполняющихся на локальной машине пользователя и систем "клиент-сервер", к которым относятся web-сайты.

Модель "клиент-сервер"

Суть модели "клиент-сервер" состоит в том, что приложение разделяется на две части, соответственно клиент и сервер. При этом каждая часть может физически выполняться на отдельной машине, что позволяет организовывать системы, распределенные в пространстве. В контексте web-сайтов эта системы выглядит следующим образом: есть web-сервер, на котором располагается ваш сайт и есть машины посетителей вашего сайта, которые в данной схеме являются клиентами. На приведена схема действия системы "клиент-сервер" в контексте web-сайта:

Figure: Модель "клиент-сервер".
Image img1601

Эта схема работает следующим образом: пользователь, через браузер посылает запрос на сервер. На стороне сервера запрос принимает программа web-сервера (в большинстве случаев - Apache), анализирует запрос и, в нашем случае, вызывает PHP для обработки этого запроса. PHP выполняет ваш скрипт, задачей которого является генерация страницы, соответствующей полученному запросу. По завершении работы PHP передает сгенерированную страницу web-серверу, который отсылает ее обратно на машину клиента как результат обработки его запроса.

Все довольно просто и понятно, за исключением одной мелочи, которая, как обычно, портит всю картину. Как правило, при создании любого более-менее сложного сайта (за исключением, может быть только домашних страничек) возникает необходимость в информации, которая сохранялась бы между запросами (например система регистрации на сайте, когда, после ввода логина/пароля необходимо "помнить" информацию о том, что пользователь зарегистрировался на сайте и является именно этим пользователем, а не каким-то другим). Однако специфика работы систем "клиент-сервер" состоит в том, что сервер не различает между собой запросы клиентов. Т.е. на приведенном рисунке все запросы, идущие со всех клиентов абсолютно равноценны. Налицо явное противоречие, и разрешение этого противоречия - суть механизма сессий.

Figure: Клиенты и сервер.
Image img1602

В основе всего механизма сессий лежит решение задачи об идентификации того, от кого именно пришел запрос на сервер. Если это будет точно известно, то уже не возникнет большой проблемы в том, чтобы предоставить скрипту информацию, относящуюся именно к этому конкретному пользователю.

Данная задача решается путем присвоения каждой сессии уникального идентификатора SID (Session IDentifier), который создается в тот момент, когда пользователь заходит на сайт, и уничтожается в момент, когда пользователь уходит с сайта. Он представляет собой строку из 32 символов (например, ac4f4a45bdc893434c95dcaffb1c1811). Этот идентификатор передается на сервер вместе с каждым запросом клиента и возвращается обратно вместе с ответом сервера. Этот идентификатор передается на сервер вместе с каждым запросом со стороны клиента и возвращается на машину клиента вместе с результатами обработки запроса. Алгоритм генерации SID (а в PHP в качестве идентификатора сессии используется т.н. GUID (Global Unique IDentifier)) позволяет гарантировать его уникальность, поэтому исключена возможность того, что две сессии будут иметь один и тот же идентификатор сессии.

PHP может использовать два различных механизма в качестве "транспортного средства" для передачи SID:

Cookies - это, конечно, более удобный способ передачи идентификатора. При этом SID сохраняется "внутри" браузера и остается незаметным для пользователя. Но поддержка cookies - это необязательное условие для браузера, она может отсутствовать или быть отключена у кого-то из посетителей, поэтому в общем случае полагаться на них нельзя. В этом случае можно использовать менее "красивый", но более надежный способ - передачу SID через параметр query string. PHP имеет возможность автоматически добавлять SID ко всем линкам в генерируемых HTML страницах, поэтому вам, как правило, не нужно будет заботиться о том, чтобы добавлять этот идентификатор к каждому линку вручную. Если же вы по каким-либо причинам хотите сами передавать идентификатор сессии - вы всегда можете получить его из константы SID или из функции session_id().

Создание и использование сессионных переменных

Для того, чтобы иметь возможность использовать сессионные переменные в своей программе, необходимо сначала создать сессию. Для этого необходимо добавить следующую строчку в ваш код:

session_start();

Пусть вас не смущает вопрос: "Что будет, если я вызову эту функцию дважды?", все будет в порядке. Вам необходимо вызывать эту функцию на каждой странице, на которой вам необходимо использовать сессионные переменные. Кроме того, если вы часто используете сессионные переменные в своих программах, вы можете просто изменить настройки PHP таким образом, чтобы сессии инициализировались автоматически.

Для наглядности сессии можно задать имя с помощью функции session_name([имя_сессии]). Делать это нужно еще до инициализации сессии. Получить имя текущей сессии можно с помощью этой же функции, вызванной без параметров: session_name();

Регистрация сессионных переменных производится путем добавления следующей строчки в ваш код:

session_register('var1','var2',...);

Зарегистрировать переменную также можно, просто записав ее значение в ассоциативный массив $_SESSION, т.е. написав

$_SESSION['имя_переменной'] = 'значение_переменой';

В этом массиве хранятся все зарегистрированные (т.е. глобальные) переменные сессии.

Доступ к таким переменным осуществляется с помощью массива $_SESSION['имя_переменной'] (или $HTTP_SESSION_VARS['имя_переменной'] для версии PHP 4.0.6 и более ранних). Если же в настройках php включена опция register_globals, то к сессионным переменным можно обращаться еще и как к обычным переменным, например так: $имя_переменной.

В качестве параметров функции передаются имена сессионных переменных, которые необходимо создать. Обратите внимание на то, что передаются именно имена, а не сами переменные, как можно подумать. Необходимо помнить об этом, дабы избежать ошибок.

После того, как сессионная переменная зарегистрирована - она может быть использована на любой странице сайта в пределах одной и той же пользовательской сессии. Повторенная регистрация переменной необязательна (т.е. зарегистрировав переменную на одной странице совершенно необязательно регистрировать ее же на всех страницах, где необходимо ее использование). Но если вы будете регистрировать переменные на каждой странице - тоже ничего страшного не произойдет.

Получить идентификатор текущей сессии можно с помощью функции session_id().

Способ обращения к сессионным переменным зависит от настроек в файле php.ini. Если register_globals включен, то обращение к сессионной переменной по синтаксису ничем не будет отличаться от обращения к обычной переменной:

session_register('myVar');
$myVar = 5;

Если же он выключен - то обращаться к сессионным переменным необходимо через глобальный массив $HTTP_SESSION_VARS:

session_register('myVar');
$HTTP_SESSION_VARS['myVar'] = 5;

Пример 1.

page1.php

<?php
session_start();
session_register('myVar');
 $myVar = 'It works! This is value of session variable'; ?>
 <html>
 <a href="page2.php">click here</a>
</html>

page2.php

<?php session_start(); echo $myVar; ?>

Пример 2.

<? session_start();
    // создаем новую сессию или
    // восстанавливаем текущую
echo session_id();
    // выводим идентификатор сессии
?>
 <html>
 <head>
 <title>My home page</title>
 </head>
 ...
 // домашняя страничка
 </html>
 <? echo session_name();
    // выводим имя текущей сессии.
    // В данном случае это PHPSESSID
?>

Авторизация доступа

Зарегистрируем логин и пароль, вводимые пользователем на странице авторизации.

<? session_start();
    // создаем новую сессию или
    // восстанавливаем текущую
if (!isset($_GET['go'])){
  echo "<form>
    Login: <input type=text name=login>
    Password: <input type=password name=passwd>
    <input type=submit name=go value=Go>
  </form>";
}else {
   $_SESSION['login']=$_GET['login'];
   // регистрируем переменную login
   $_SESSION['passwd']=$_GET['passwd'];
   // регистрируем переменную passwd
// теперь логин и пароль - глобальные
// переменные для этой сессии
    if ($_GET['login']=="pit" &&
        $_GET['passwd']=="123") {
        Header("Location: secret_info.php");
        // перенаправляем на страницу
        // secret_info.php
    }else echo "Неверный ввод,
                попробуйте еще раз<br>";
} print_r($_SESSION);
    // выводим все переменные сессии
?>

Теперь, попав любую страницу сайта, мы сможем работать с введенными пользователем логином и паролем, которые будут храниться в массиве $_SESSION. Таким образом, если изменить код секретной странички (secret_info.php) так:

<?php session_start();
    // создаем новую сессию или
    // восстанавливаем текущую
print_r($_SESSION);
    // выводим все переменные сессии
?>
<html>
<head>
<title>Secret info</title>
</head>
<body>
<p>Здесь я хочу делиться секретами с другом Васей.
</body>
</html>

То мы получим в браузере на секретной странице следующее:

Array (
[login] => pit
[passwd] => 123 )
 Здесь я хочу делиться секретами с другом Васей.

В итоге получим.список переменных и саму секретную страничку.

Что это нам дает? Допустим, злобный хакер хочет прочитать секреты. И он как-то хитрым способом узнал, как называется секретная страничка (или странички). Тогда он может подло попытаться просто ввести ее адрес в строке браузера, минуя страницу авторизации (ввода пароля). Чтобы избежать такого гнусного проникновения в наши тайны, нужно дописать всего пару строк в код секретных страничек:

<?php session_start();
    // создаем новую сессию или
    // восстанавливаем текущую
print_r($_SESSION);
    // выводим все переменные сессии
if (!($_SESSION['login']=="pit" &&
    $_SESSION['passwd']==123))
    // проверяем правильность
    // пароля-логина
    Header("Location: authorize.php");
    // если ошибка, то перенаправляем на
    // страницу авторизации
?> <html> <head><title>Secret info</title></head> ... // здесь
располагается
    //секретная информация :)
</html>

Удаление переменных сессии

Кроме умения регистрировать переменные сессии (т.е. делать их глобальными на протяжении всего сеанса работы), полезно также уметь удалять такие переменные и сессию в целом.

Функция session_unregister(имя_переменной) удаляет глобальную переменную из текущей сессии (т.е. удаляет ее из списка зарегистрированных переменных). Если регистрация производилась с помощью $_SESSION ($HTTP_SESSION_VARS для версии PHP 4.0.6 и более ранних), то используют языковую конструкцию unset(). Она не возвращает никакого значения, а просто уничтожает указанные переменные.

Где это может пригодиться? Например, для уничтожения данных о посетителе (в частности, логина и пароля) после его ухода с секретной странички. Если правильные логин и пароль сохранятся и окно браузера после посещения сайта не закрыли, то любой другой пользователь этого компьютера сможет прочитать закрытую информацию.

В файл secret_info.php добавим строчку для выхода на главную страницу:

<?php
// ... php код
?>
<html>
<head>
<title>Secret info</title>
</head>
...
// здесь располагается
// секретная информация :)
<a href="index.php">На главную</a>
</html>
В Index.php уничтожим логин и пароль, введенные ранее:
<? session_start();
session_unregister('passwd');
    // уничтожаем пароль
unset($_SESSION['login']);
    // уничтожаем логин
print_r($_SESSION);
    // выводим глобальные переменные сессии
?>
<html>
<head>
<title>My home page</title>
</head>
... // домашняя страничка </html>

Теперь, чтобы попасть на секретную страницу, нужно будет опять вводить логин и пароль.

Для того чтобы сбросить значения всех переменных сессии, можно использовать функцию session_unset();

Уничтожить текущую сессию целиком можно командой session_destroy(); Она не сбрасывает значения глобальных переменных сессии и не удаляет cookies, а уничтожает все данные, ассоциируемые с текущей сессией.

<? session_start(); // инициализируем сессию $test = "Переменная
сессии"; $_SESSION['test']= $test;
// регистрируем переменную $test.
// если register_globals=on,
// то можно использовать
// session_register('test');

print_r($_SESSION);
// выводим все глобальные переменные

echo session_id();
// выводим идентификатор сессии

echo "<hr>"; session_unset();
// уничтожаем все глобальные
// переменные сессии
print_r($_SESSION); echo session_id(); echo "<hr>";
session_destroy(); // уничтожаем сессию print_r($_SESSION); echo
session_id(); ?>

В результате работы этого скрипта будут выведены три строки: в первой - массив с элементом test и его значением, а также идентификатор сессии, во второй - пустой массив и идентификатор сессии, в третьей - пустой массив. Таким образом, видно, что после уничтожения сессии уничтожается и ее идентификатор, и мы больше не можем ни регистрировать переменные, ни вообще производить какие-либо действия с сессией.

Безопасность

Вообще говоря, cледует понимать, что использование механизма сессий не гарантирует полной безопасности системы. Для этого нужно принимать дополнительные меры. Обратим внимание на проблемы с безопасностью, которые могут возникнуть при работе с сессиями и, в частности, с теми программами, что мы написали.

Во-первых, опасно передавать туда-сюда пароль, его могут перехватить. Кроме того, мы зарегистрировали его как глобальную переменную сессии, значит, он сохранился в cookies на компьютере-клиенте. Это тоже плохо. И вообще, пароли и логины по-хорошему должны храниться в базе данных.

Задания на лабораторную работу

  1. Реализуйте предложенные примеры.
  2. Найдите в документации описание вывода отдельного системного окна для ввода имени пользователя и пароля. Реализуйте аутенификацию с выдачей системного окна.
  3. В текстовом файле хранится информация о пользователях в виде:
    имя_пользователя:пароль:ключевое_слово
    
    Организуйте аутенификацию с чтением данных о правильности имени и пароля из файла. При неправильном вводе должно быть выдано сообщение о неправильном имени пользователя или пароле. Если пользователь правильно ввел имя_пользователя, а пароль неверно отобразите его пароль после правильного ввода ключевого слова.