По бездействию пользователя на php. Блокировка PHP сессий

Приветствую, уважаемое сообщество.

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

Цель этой статьи - осветить подводные камни использования сессий в PHP. Конечно, есть документация по PHP и масса примеров, и данная статья не претендует на полное руководство. Она призвана раскрыть некоторые ньюансы работы с сессиями и оградить разработчиков от ненужной траты времени.

Самым распространенным примером использования сессий является, конечно, авторизация пользователей. Начнем с самой базовой реализации, чтобы последовательно развивать ее по мере появления новых задач.

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

Function startSession() { // Если сессия уже была запущена, прекращаем выполнение и возвращаем TRUE // (параметр session.auto_start в файле настроек php.ini должен быть выключен - значение по умолчанию) if (session_id()) return true; else return session_start(); // Примечание: До версии 5.3.0 функция session_start()возвращала TRUE даже в случае ошибки. // Если вы используете версию ниже 5.3.0, выполняйте дополнительную проверку session_id() // после вызова session_start() } function destroySession() { if (session_id()) { // Если есть активная сессия, удаляем куки сессии, setcookie(session_name(), session_id(), time()-60*60*24); // и уничтожаем сессию session_unset(); session_destroy(); } }

Примечание: Подразумевается, что базовые знания о сессиях PHP у читателя имеются, поэтому принцип работы функций session_start() и session_destroy() освещать здесь не будем. Задачи верстки формы входа и аутентификации пользователя не относятся к теме статьи, поэтому их мы также опустим. Напомню только, что для идентификации пользователя в каждом последующем запросе, нам необходимо в момент успешного входа сохранить в сессионной переменной (с именем userid, например) идентификатор пользователя, который будет доступен во всех последующих запросах в пределах жизни сессии. Также необходимо реализовать обработку результата нашей функции startSession(). Если функция вернула FALSE - отобразить в браузере форму входа. Если функция вернула TRUE, и сессионная переменная, содержащая идентификатор авторизованного пользователя (в нашем случае - userid), существует - отобразить страницу авторизованного пользователя (подробнее об обработке ошибок см. дополнение от 2013-06-07 в разделе о сессионных переменных).

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

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

Function startSession() { // Таймаут отсутствия активности пользователя (в секундах) $sessionLifetime = 300; if (session_id()) return true; // Устанавливаем время жизни куки ini_set("session.cookie_lifetime", $sessionLifetime); // Если таймаут отсутствия активности пользователя задан, устанавливаем время жизни сессии на сервере // Примечание: Для production-сервера рекомендуется предустановить эти параметры в файле php.ini if ($sessionLifetime) ini_set("session.gc_maxlifetime", $sessionLifetime); if (session_start()) { setcookie(session_name(), session_id(), time()+$sessionLifetime); return true; } else return false; }

Немного пояснений. Как известно, PHP определяет, какую именно сессию нужно запустить, по имени куки, передаваемом браузером в заголовке запроса. Браузер же, в свою очередь, получает этот куки от сервера, куда помещает его функция session_start(). Если время жизни куки в браузере истекло, он не будет передан в запросе, а значит PHP не сможет определить, какую сессию нужно запустить, и расценит это как создание новой сессии. Параметр настроек PHP session.gc_maxlifetime, который устанавливается равным нашему таймауту отсутствия активности пользователя, задает время жизни PHP-сессии и контролируется сервером. Работает контроль времени жизни сессии следующим образом (здесь рассматривается пример хранилища сессий во временных файлах как самый распространенный и установленный по умолчанию в PHP вариант).

В момент создания новой сессии в каталоге, установленном как каталог для хранения сессий в параметре настроек PHP session.save_path, создается файл с именем sess_, где - идентификатор сессии. Далее, в каждом запросе, в момент запуска уже существующей сессии, PHP обновляет время модификации этого файла. Таким образом, в каждом следующем запросе PHP, путем разницы между текущим временем и временем последней модификации файла сессии, может определить, является ли сессия активной, или ее время жизни уже истекло. (Механизм удаления старых файлов сессий более подробно рассматривается в следующем разделе).

Примечание: Здесь следует отметить, что параметр session.gc_maxlifetime действует на все сессии в пределах одного сервера (точнее, в пределах одного главного процесса PHP). На практике это значит, что если на сервере работает несколько сайтов, и каждый из них имеет собственный таймаут отсутствия активности пользователей, то установка этого параметра на одном из сайтов приведет к его установке и для других сайтов. То же касается и shared-хостинга. Для избежания подобной ситуации используются отдельные каталоги сессий для каждого сайта в пределах одного сервера. Установка пути к каталогу сессий производится с помощью параметра session.save_path в файле настроек php.ini, или путем вызова функции ini_set(). После этого сессии каждого сайта будут храниться в отдельных каталогах, и параметр session.gc_maxlifetime, установленный на одном из сайтов, будет действовать только на его сессии. Мы не станем рассматривать этот случай подробно, тем более, что у нас в запасе есть более гибкий вариант контроля отсутствия активности пользователя.

Контроль отсутствия активности пользователя с помощью сессионных переменных Казалось бы, предыдущий вариант при всей своей простоте (всего пару дополнительных строк кода) дает все, что нам нужно. Но что, если не каждый запрос можно расценивать как результат активности пользователя? Например, на странице установлен таймер, который периодически выполняет AJAX-запрос на получение обновлений от сервера. Такой запрос нельзя расценивать как активность пользователя, а значит автоматическое продление времени жизни сессии является не корректным в данном случае. Но мы знаем, что PHP обновляет время модификации файла сессии автоматически при каждом вызове функции session_start(), а значит любой запрос приведет к продлению времени жизни сессии, и таймаут отсутствия активности пользователя не наступит никогда. К тому же, последнее примечание из предыдущего раздела о тонкостях работы параметра session.gc_maxlifetime может показаться кому-то слишком запутанным и сложным в реализации.

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

Function startSession($isUserActivity=true) { $sessionLifetime = 300; if (session_id()) return true; // Устанавливаем время жизни куки до закрытия браузера (контролировать все будем на стороне сервера) ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; $t = time(); if ($sessionLifetime) { // Если таймаут отсутствия активности пользователя задан, // проверяем время, прошедшее с момента последней активности пользователя // (время последнего запроса, когда была обновлена сессионная переменная lastactivity) if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) { // Если время, прошедшее с момента последней активности пользователя, // больше таймаута отсутствия активности, значит сессия истекла, и нужно завершить сеанс destroySession(); return false; } else { // Если таймаут еще не наступил, // и если запрос пришел как результат активности пользователя, // обновляем переменную lastactivity значением текущего времени, // продлевая тем самым время сеанса еще на sessionLifetime секунд if ($isUserActivity) $_SESSION["lastactivity"] = $t; } } return true; }

Подытожим. В каждом запросе мы проверяем, не достигнут ли таймаут с момента последней активности пользователя до текущего момента, и если он достигнут - уничтожаем сессию и прерываем выполнение функции, возвращая FALSE. Если же таймаут не достигнут, и в функцию передан параметр $isUserActivity со значением TRUE - обновляем время последней активности пользователя. Все, что нам остается сделать - это определять в вызывающем скрипте, является ли запрос результатом активности пользователя, и если нет - вызывать функцию startSession со значением параметра $isUserActivity, равным FALSE.

Дополнение от 2013-06-07 Обработка результата функции sessionStart()

В комментариях обратили внимание на то, что возврат FALSE не дает полного понимания причины ошибки, и это абсолютно справедливо. Я не стал публиковать здесь подробную обработку ошибок (объем статьи и так не маленький), поскольку это не относится напрямую к теме статьи. Но учитывая комментарии, внесу ясность.

Как видно, функция sessionStart может вернуть FALSE в двух случаях. Либо сессию не удалось запустить из-за каких-то внутренних ошибок сервера (например, неправильные настройки сессий в php.ini), либо время жизни сессии истекло. В первом случае мы должны перебросить пользователя на страницу с ошибкой о том, что есть проблемы на сервере, и формой обращения в службу поддержки. Во втором случае мы должны перевести пользователя на форму входа и вывести в ней соответствующее сообщение о том, что время сессии истекло. Для этого нам необходимо ввести коды ошибок и возвращать вместо FALSE соответствующий код, а в вызывающем методе проверять его и действовать соответствующим образом.

Теперь, даже если сессия на сервере по-прежнему существует, она будет уничтожена при первом же обращении к ней, если таймаут отсутствия активности пользователя истек. И это произойдет независимо от того, какое время жизни сессий установлено в глобальных настройках PHP.

Примечание: А что произойдет, если браузер был закрыт, и куки с именем сессии был автоматически уничтожен? Запрос к серверу при следующем открытии браузера не будет содержать куки сессии, и сервер не сможет открыть сессию и проверить таймаут отсутствия активности пользователя. Для нас это равносильно созданию новой сессии и никак не влияет на функционал и безопасность. Но возникает справедливый вопрос - а кто же тогда уничтожит старую сессию, если до сих пор ее уничтожали мы по истечении таймаута? Или она теперь будет висеть в каталоге сессий вечно? Для очистки старых сессий в PHP существует механизм под названием garbage collection. Он запускается в момент очередного запроса к серверу и чистит все старые сессии на основании даты последнего изменения файлов сессий. Но запуск механизма garbage collection происходит не при каждом запросе к серверу. Частота (а точнее, вероятность) запуска определяется двумя параметрами настроек session.gc_probability и session.gc_divisor. Результат от деления первого параметра на второй и есть вероятностью запуска механизма garbage collection. Таким образом, для того, чтобы механизм очистки сессий запускался при каждом запросе к севреру, эти параметры нужно установить в равные значения, например «1». Такой подход гарантирует чистоту каталога сессий, но, очевидно, является слишком накладным для сервера. Поэтому в production-системах по умолчанию устанавливается значение session.gc_divisor, равное 1000, что означает, что механизм garbage collection будет запускаться с вероятностью 1/1000. Если вы поэкспериментируете с этими настройками в своем файле php.ini, то сможете заметить, что в описанном выше случае, когда браузер закрывается и очищает все свои куки, в каталоге сессий какое-то время все еще остаются старые сессии. Но это не должно вас волновать, т.к. как уже было сказано, это ни коим образом не влияет на безопасность нашего механизма.

Дополнение от 2013-06-07 Предотвращение зависания скриптов из-за блокировки файла сессии

В комментариях подняли вопрос о зависании одновременно выполняющихся скриптов из-за блокировки файла сессии (как самый яркий вариант - long poll).

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

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

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

Защита сессий от несанкционированного использования Представим себе ситуацию. Один из ваших пользователей цепляет троян, который грабит куки браузера (в котором хранится наша сессия) и отправляет его на указанный email. Злоумышленник получает куки и использует его для подделки запроса от имени нашего авторизованного пользователя. Сервер успешно принимает и обрабатывает этот запрос, как если бы он пришел от авторизованного пользователя. Если не реализована дополнительная проверка IP-адреса, такая атака приведет к успешному взлому аккаунта пользователя со всеми вытекающими последствиями.

Почему это стало возможным? Очевидно, потому что имя и идентификатор сессии всегда одни и те же на все время жизни сессии, и если получить эти данные, то можно беспрепятственно слать запросы от имени другого пользователя (естественно, в пределах времени жизни этой сессии). Возможно, это не самый распространенный вид атак, но теоретически все выглядит вполне реализуемым, особенно учитывая, что подобному трояну даже не нужны права администратора, чтобы грабить куки браузера пользователя.

Как же можно защититься от атак подобного рода? Опять-таки, очевидно, ограничив время жизни идентификатора сессии и периодически изменяя идентификатор в пределах одной сессии. Мы можем также изменять и имя сессии, полностью удаляя старую и создавая новую сессию, копируя в нее все сессионные переменные из старой. Но на суть подхода это не влияет, поэтому для простоты ограничимся только идентификатором сессии.

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

(Опустим ту часть кода, которая уже рассмотрена).

Function startSession($isUserActivity=true) { // Время жизни идентификатора сессии $idLifetime = 60; ... if ($idLifetime) { // Если время жизни идентификатора сессии задано, // проверяем время, прошедшее с момента создания сессии или последней регенерации // (время последнего запроса, когда была обновлена сессионная переменная starttime) if (isset($_SESSION["starttime"])) { if ($t-$_SESSION["starttime"] >= $idLifetime) { // Время жизни идентификатора сессии истекло // Генерируем новый идентификатор session_regenerate_id(true); $_SESSION["starttime"] = $t; } } else { // Сюда мы попадаем, если сессия только что создана // Устанавливаем время генерации идентификатора сессии в текущее время $_SESSION["starttime"] = $t; } } return true; }

Итак, при создании новой сессии (которое происходит в момент успешного входа пользователя), мы устанавливаем сессионную переменную starttime, хранящую для нас время последней генерации идентификатора сессии, в значение, равное текущему времени сервера. Далее в каждом запросе мы проверяем, не прошло ли достаточно времени (idLifetime) с момента последней генерации идентификатора, и если прошло - генерируем новый. Таким образом, если в течение установленного времени жизни идентификатора злоумышленник, получивший куки авторизованного пользователя, не успеет им воспользоваться, поддельный запрос будет расценен сервером как неавторизованный, и злоумышленник попадет на страницу входа.

Примечание: Новый идентификатор сессии попадает в куки браузера при вызове функции session_regenerate_id(), которая отправляет новый куки, аналогично функции session_start(), поэтому нам нет необходимости обновлять куки самостоятельно.

Если мы хотим максимально обезопасить наши сессии, достаточно установить время жизни идентификатора в единицу или же вообще вынести функцию session_regenerate_id() за скобки и убрать все проверки, что приведет к регенерации идентификатора в каждом запросе. (Я не проверял влияние такого подхода на быстродействие, и могу только сказать, что функция session_regenerate_id(true) выполняет по сути всего 4 действия: генерация нового идентификатора, создание заголовка с куки сессии, удаление старого и создание нового файла сессии).

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

Возможность одновременной работы в одном браузере от имени нескольких пользователей Последняя задача, которую хотелось бы рассмотреть - возможность одновременной работы в одном браузере нескольких пользователей. Эта возможность особенно полезна на этапе тестирования, когда нужно эмулировать одновременную работу пользователей, и желательно делать это в своем любимом браузере, а не использовать весь доступный арсенал или открывать несколько экземпляров браузера в режиме «инкогнито».

В наших предыдущих примерах мы не задавали явно имя сессии, поэтому использовалось имя, установленное в PHP по умолчанию (PHPSESSID). Это значит, что все сессии, которые создавались нами до сих пор, отправляли браузеру куки под именем PHPSESSID. Очевидно, что если имя куки всегда одинаковое, то нет возможности в пределах одного браузера организовать две сессии с одинаковым именем. Но если бы мы для каждого пользователя использовали собственное имя сессии, то проблема была бы решена. Так и сделаем.

Function startSession($isUserActivity=true, $prefix=null) { ... if (session_id()) return true; // Если в параметрах передан префикс пользователя, // устанавливаем уникальное имя сессии, включающее этот префикс, // иначе устанавливаем общее для всех пользователей имя (например, MYPROJECT) session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; ... }

Теперь осталось позаботиться о том, чтобы вызывающий скрипт передавал в функцию startSession() уникальный префикс для каждого пользователя. Это можно сделать, например, через передачу префикса в GET/POST параметрах каждого запроса или через дополнительный куки.

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

Function startSession($isUserActivity=true, $prefix=null) { $sessionLifetime = 300; $idLifetime = 60; if (session_id()) return true; session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; $t = time(); if ($sessionLifetime) { if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) { destroySession(); return false; } else { if ($isUserActivity) $_SESSION["lastactivity"] = $t; } } if ($idLifetime) { if (isset($_SESSION["starttime"])) { if ($t-$_SESSION["starttime"] >= $idLifetime) { session_regenerate_id(true); $_SESSION["starttime"] = $t; } } else { $_SESSION["starttime"] = $t; } } return true; } function destroySession() { if (session_id()) { session_unset(); setcookie(session_name(), session_id(), time()-60*60*24); session_destroy(); } }

Надеюсь, эта статья сэкономит немного времени тем, кто никогда особо не углублялся в механизм сессий, и даст достаточно понимания этого механизма тем, кто только начинает знакомиться с PHP.

Будем учиться делать простую аутентификацию пользователей на сайте. На сайте могут быть страницы только для авторизованных пользователей и они будут полноценно функционировать, если добавить к ним наш блок аутентификации. Чтобы его создать, нужна база данных MySQL. Она может иметь 5 колонок (минимум), а может и больше, если вы хотите добавить информацию о пользователях. Назовём базу данных “Userauth”.

Создадим в ней следующие поля: ID для подсчёта числа пользователей, UID для уникального идентификационного номера пользователя, Username для имени пользователя, Email для адреса его электронной почты и Password для пароля. Вы можете использовать для авторизации пользователя и уже имеющуюся у Вас базу данных, только, как и в случае с новой базой данных, создайте в ней следующую таблицу.

Код MySQL

CREATE TABLE `users` (`ID` int (11) NOT NULL AUTO_INCREMENT, `UID` int (11) NOT NULL, `Username` text NOT NULL, `Email` text NOT NULL, `Password` text NOT NULL, PRIMARY KEY (`ID`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Теперь создадим файл "sql.php". Он отвечает за подключение к базе данных. Данный код, во первых, создаёт переменные для сервера и пользователя, когда он подключается к серверу. Во-вторых, он выберет базу данных, в данном случае "USERAUTH". Этот файл нужно подключить в "log.php" и "reg.php" для доступа к базе данных.

Код PHP

Далее страница входа, пусть она называется "login.php". Во-первых, она проверяет введённые данные на наличие ошибок. Страница имеет поля для имени пользователя, пароля, кнопку отправки и ссылку для регистрации. Когда пользователь нажмёт кнопку «Вход», форма будет обработана кодом из файла "log.php", а затем произойдёт вход в систему.

Код PHP

Форма входа

Имя пользователя
Пароль
Регистрация

Затем пишем скрипт для входа в систему. Назовём его "log.php". Он имеет функцию для очистки входных данных от SQL-инъекций, которые могут испортить ваш скрипт. Во-вторых, он получает данные формы и проверяет их на правильность. Если входные данные правильны, скрипт отправляет пользователя на страницу авторизованных пользователей, если нет – устанавливает ошибки и отправляет пользователя на страницу входа.

Код PHP

Сделаем страницу регистрации, назовём её "register.php". Она похожа на страницу входа, только имеет на несколько полей больше, а вместо ссылки на регистрацию – ссылку на login.php на случай, если у пользователя уже есть аккаунт.

Код PHP

Форма регистрации

Имя пользователя
E-mail
Пароль
Повтор пароля
У меня есть аккаунт

Теперь сделаем скрипт регистрации в файле "reg.php". В него будет включён "sql.php" для подключения к к базе данных. Используется и та же функция, что и в скрипте входа для очистки поля ввода. Устанавливаются переменные для возможных ошибок. Далее – функция для создания уникального идентификатора, который никогда ранее не предоставлялся. Затем извлекаются данные из формы регистрации и проверяются. Происходит проверка, что адрес электронной почты указан в нужном формате, а также, правильно ли повторно указан пароль. Затем скрипт проверяет, нет ли в базе данных пользователя с таким же именем, и, если есть, сообщает об ошибке. И, наконец, код добавляет пользователя в базу данных.

Код PHP

Ещё нужно сделать скрипт для выхода пользователя из системы. Он прекращает сессию для пользователя с данным уникальным идентификатором и именем, а затем перенаправляет пользователя на страницу входа в систему.

Код PHP

И, наконец, скрипт "auth.php" можно использовать, чтобы сделать страницы доступными только для авторизованных пользователей. Он проверяет данные входа и, если они верны, позволяет пользователю просматривать страницы, а если нет, просит авторизоваться. Кроме того, если кто-то попытается взломать сайт создав одну из сессий, она будет прервана, как в общем случае.

Код PHP

Одно из условий в коде выше является предметом вопроса в .

Следующий код нужно вставить на страницу для авторизованных пользователей, она называется, например, "member.php", а у Вас может называться как угодно.

Код PHP

Вам разрешён доступ к этой странице. Выйти ( )

Аутентификация пользователей готова!

Сессии - это механизм, позволяющий хранить определённые данные на сервере, уникальные для каждого пользователя. Особо внимательные читатели обнаружат сходство с cookie . И, в целом, это одно и то же. Однако, ключевой момент: данные хранятся не в браузере пользователя, а в специальном файле на сервере , имя которого уникально для каждого пользователя. А уникальный идентификатор сессии PHP хранится уже в cookie .

Давайте с Вами поработаем с сессиями в PHP . И начнём с функции session_start() . Данная функция делает следующее: если пользователь зашёл впервые, то создаёт уникальный идентификатор и записывает его в cookie , а также создаёт новый файл, вновь уникальный для пользователя. Если пользователь уже заходил, то тогда сервер считывает значение уникального идентификатора из cookie и, в соответствии с ним, обращается к нужному файлу сессии. Из этого файла PHP считывает все данные и помещает их в массив $_SESSION . Давайте напишем простой код, в котором мы записываем переменную в сессию, либо считываем, если она уже была записана.

Вначале мы вызываем функцию session_start() , которую я описал выше. Затем проверяем: существует ли переменная "name " в сессии. Если существует, то считываем из неё данные и записываем в переменную name . Если не существует (то есть пользователь пришёл в первый раз), то устанавливаем переменной "name " в сессии значение "15St. ". Следующей строкой выводим значение переменной $name . Очевидно, что при первом запуске, Вы увидите пустую строку, а вот при втором увидите строку "15St. ", считанную из сессии.

Советую Вам сейчас ввести в адресной строке: "javascript:document.cookie " (вводите на той же вкладке, что и запускали скрипт). В результате, Вы увидите примерно следующее: "PHPSESSID=". Как раз значение PHPSESSID и является тем самым уникальный идентификатором.

И чтобы всё стало совсем понятно, то советую даже найти файл сессии . Если Вы используете Denwer , то он лежит в папке "tmp ". Посмотрите на файлы, которые начинаются на "sess_ " - это и есть те самые файлы сессии . Вы можете их открыть в простом блокноте.

Ещё одно очень важное свойство - это временность сессий в PHP . То если cookie хранятся до тех пор, пока их не удалит браузер. А браузер их по умолчанию не удаляет никогда. То сессии хранятся время, заданное в настройках PHP . По умолчанию, это 15 минут. То есть если Вы будете использовать аутентификацию на основе сессии , то через 15 минут бездействия пользователя, ему снова придётся авторизоваться. Разумеется, это хорошо, так как если пользователь забудет "Выйти ", то ничего плохого не случится. Злоумышленник не сможет воспользоваться аккаунтом пользователя. Более того, при использовании cookie их можно выкрасть, подставить в своём браузере, и в результате злоумышленник авторизован под чужими данными, не зная даже пароля. А сессию украсть не получится, так как все параметры хранятся на сервере, и узнать о них не получится.

Поэтому старайтесь в своей практике преимущественно использовать сессии , а не чистые cookie .

И, напоследок, хочется предупредить об очень частой ошибке. Никогда не выводите данные в браузеры до использования функции session_start() , иначе она выдаст ошибку. То есть нельзя писать вот так:

При запуске данного скрипта возникнет ошибка. То же самое правило действовало и с cookie (функция setcookie() ). Так что, думаю, здесь всё понятно.

Что же касается сессий в PHP , то, разумеется, их можно использовать для хранения данных о статистике, аутентификации, личных настроек пользователя и других аналогичных вещей.

Доброго времени суток друзья! Давай с Вами рассмотрим регистрацию пользователей на PHP. Для начала давайте определим условия для нашей регистрации пользователей:

  • Пароль шифруем при помощи алгоритма MD5
  • Пароль будем "солить"
  • Проверка на занятость Логина
  • Активация пользователя письмом.
  • Запись и хранение данных в СУБД MySQL

Для написание данного скрипта нам нужно понять, что такое регистрация пользователя. Регистрация пользователя - это получения данных реального пользователя, обработка и хранение данных.

Если объяснять простыми словами то регистрация это всего лишь запись и хранение определенных данных по которым мы можем авторизировать пользователя в нашем случае - это Логин и Пароль.

Авторизация — предоставление определённому лицу или группе лиц прав на выполнение определённых действий, а также процесс проверки данных прав при попытке выполнения этих действий. Проше говоря с помощью авторизации мы можем разграничить доступ к тому или иному контенту на нашем сайте.

Рассмотрим структуру каталогов скриптов для реализации нашей регистрации с авторизацией. Нам нужно разбить скрипты на логические составляющие. Модули регистрации и авторизации мы поместив в отдельный каталог. Так же в отдельные каталоги мы поместим подключение к базе данных MySQL , файл с пользовательскими функциями, файл стилей CSS и наш шаблон HTML . Данная структура позволяет быстро ориентироваться в скриптах. Представьте себе, что у Вас большой сайт с кучей модулями и т.д. и если не будет порядка, то будет очень сложно что-то отыскать в таком бардаке.

Так как мы будем хранить все данные в СУБД MySQL , то давайте создадим не большую таблицу в которой будем хранить данные о регистрации.

Для начала нужно создать таблицу в базе данных. Таблицу назовем bez_reg где bez - это префикс таблицы, а reg название таблицы.

Структура таблицы: bez_reg -- -- Структура таблицы `bez_reg` -- CREATE TABLE IF NOT EXISTS `bez_reg` (`id` int(11) NOT NULL AUTO_INCREMENT, `login` varchar(200) NOT NULL, `pass` varchar(32) NOT NULL, `salt` varchar(32) NOT NULL, `active_hex` varchar(32) NOT NULL, `status` int(1) NOT NULL, PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; Теперь создадим основные скрипты для дальнейшей работы. Файл INDEX.PHP

Файл CONFIG.PHP

less/reg/?mode=auth">Войти