Сессии в PHP

В разнообразных конференциях, посвященных программированию меня в первую очередь всегда интересуют такие разделы, как "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?>, ты на секретной странице!!! : <!--/ Полоса /--->
     



  • Опубликовал admin
    10 Сен, Среда 2003г.



    Мазь Yiganerjing
    Мазь Yiganerjing
    sunny-asia.ru
    Программирование для чайников.