Активни сесии. Сесии

10.08.2022

Сесия (от латински - sessio - среща, английски - сесия) е период от време, обхващащ работата на потребителя в Интернет от момента на отваряне на първата връзка до последната. Изчислява се като времевата разлика между първоначалната и крайната заявка. Последната страница обаче може да отнеме различно време, за да бъде прегледана от потребителя, което следователно прави измерването на времето между две заявки по-трудно.

Как една сесия е свързана с HTTP протокола и БИСКВИТКИТЕ?

Какво е сесия може да се обясни въз основа на HTTP протокола. Сам по себе си този протокол няма начин да запази състоянието между две операции. Тоест, просто казано, отваряйки една страница и след това преминавайки от нея към друга, HTTP няма да може да установи, че и двете заявки принадлежат на един и същ потребител. И тук на помощ идва специален метод за проследяване - управление на сесии (нашите сесии).
Следователно, отговаряйки на въпроса какво е сесия, можем да кажем, че това е спомагателен логически обект, който улеснява прехвърлянето на данни между последователни HTTP заявки от един потребител.
Бисквитките, подобно на сесиите, съхраняват информация за потребителя, докато той навигира в различни страниции подобряване на производителността на протокола. Но за разлика от втория, където данните се съхраняват във временни файлове на сървъра, те ги записват на компютъра на потребителя под формата на малки фрагменти.

За какво са сесиите?

Използването на сесии става незаменимо при работа със сайтове като форуми, табла за съобщения и онлайн магазини, тъй като в този случай е необходимо да се запазят данни за потребителя на няколко страници.

Етапи на сесията

Цялата сесия може да бъде разделена на три етапа:

  • отваряне на сесия (когато потребителят започне да работи с определен сайт),
  • отчитане на променливите на сесията (при преминаване към различни страници),
  • край на сесията.

Поради факта, че данните за сесията се съхраняват на сървър на трета страна, тогава е най-добре да не съхранявате големи количества информация в тях, а да използвате бисквитки.

За правилното показване на тази страница ви е необходим браузър с поддръжка на JavaScript.

Сесии за оторизация на потребителя

За достъп до него трябва да следвате връзката Активни потребителиВ глава Администрация – Начална страница в блока Потребители(фиг. 2); връзка Активни потребителиВ глава Администрация – Система – Лицензив редица Конкурентни лицензи(фиг. 3) - тази опция е възможна само ако използва системата ELMAвид конкурентни лицензи ; натискане на бутон Активни потребителиВ глава Администрация - Потребители(фиг. 4).

Ориз. 2. Секция "Администрация - Потребители". Бутон за активни потребители

Ориз. 3. Раздел "Администрация - Система - Лицензи". Връзка за активни потребители

Ориз. 4. Раздел "Администрация - Начална страница". Връзка за активни потребители

Потребителско име– това поле съдържа пълното име. потребител в системата ЕЛМА. Бутонът до името на потребителя ви позволява да прекратите (излезте от съответния потребител) и да премахнете информация за всички сесии на този потребител от списъка.

IP адрес– това поле съдържа IP адреса на потребителя, от който е извършена авторизация в системата. Ако уеб фермата ELMA се използва за работа, техните реални IP адреси ще бъдат показани за всеки активен потребител.

Бутонът до IP адреса ви позволява да прекратите съответната сесия и да премахнете информацията за нея от списъка.

Иконата до IP адреса означава, че текущият потребител в момента е оторизиран в системата. След като изтече определено време от последния отговор на потребителя, активната сесия се прекратява. Поставените на пауза сесии са маркирани с икона. Периодът на изчакване, след който активната сесия се спира, е дефиниран в раздел Администриране – Система – Системни настройки – Настройки за сигурност.

Последен отговор– това поле съдържа информация за последното действие, извършено от потребителя в системата или за последната успешно изпратена заявка от страницата към сървъра (веднъж на минута се изпраща уеб заявка от отворената в браузъра страница към ELMA сървъра до проверете връзката и съберете статистика).

Последно действие на потребителя– това поле съдържа информация за последното извършено от потребителя действие в системата във формат Дата – Час – Връзка към последната страница, посетена от потребителя. Тази връзка е относителна. За получаване пълен линктрябва да добавите адреса на сървъра към него (фиг. 5).

Ориз. 5. Формиране на пълния адрес на връзката към последната посетена от потребителя страница

Ако няма потребителска активност за определено време, текущата сесия се прекратява автоматично. Настройките за продължителност на съхранението на сесията се конфигурират в раздела

Много потребители оценяват безпроблемната синхронизация между устройствата в Telegram. Поради факта, че собствените приложения на този междуплатформен месинджър са инсталирани на всички операционна система(настолен компютър и мобилно устройство), хората могат да влизат в своите акаунти от няколко устройства едновременно.

В скорошна актуализация на Telegram, в допълнение към визуализациите на връзки, беше създаден нов раздел в настройките за сигурност и поверителност на Telegram. Нарича се „Активни сесии“.

Разделът съдържа информация за IP адреса на потребителя на Telegram и активните сесии. Всеки има възможност да прекрати неизползвани сесии, както и тези активни сесии, които изглеждат съмнителни.

Упълномощаване на два етапа

Друго нововъведение също засяга раздела за сигурност и поверителност. Това е авторизация в Telegram на два етапа (двуетапна авторизация).

Тя ви позволява да създадете парола, която (в допълнение към потвърждението чрез SMS) ще бъде изисквана всеки път, когато влезете от ново устройство.

Но трябва да внимавате с това: ако създадената парола се загуби, собственикът сметканяма да може да влезе в собствения си акаунт в Telegram от друга джаджа.

  • конфигурирайте възможността за възстановяване на вашата парола чрез имейл;

Уеб сървърът не поддържа постоянна връзка с клиента и всяка заявка се обработва като нова, без връзка с предишните.
Тоест не можете нито да проследявате заявки от един и същи посетител, нито да запазвате променливи за него между изгледите отделни страници. Именно за решаването на тези два проблема са измислени сесиите.
Всъщност сесиите, накратко, са механизъм, който ви позволява да идентифицирате уникално браузър и създава файл за този браузър на сървъра, в който се съхраняват променливи на сесията.

Няма да описвам подробно необходимостта от такъв механизъм. Това са учебникарски казуси като пазарска количка в електронен магазин, авторизация, както и не съвсем тривиални проблеми като защитата на интерактивни части на сайта от спам.

По принцип е доста лесно да направите свой собствен аналог на сесии, не толкова функционален, колкото вградения в PHP, но подобен по същество. Относно бисквитките и базата данни.
Когато изискваме скрипт, гледаме дали е получена бисквитка с конкретно име. Ако няма бисквитка, задайте я и я запишете в базата данни нова линияс потребителски данни. Ако има бисквитка, ние четем данните от базата данни. С друга заявка изтриваме стари записи от базата данни и вече имаме готов механизъм за сесии. Не е никак трудно. Но има някои нюанси, които правят за предпочитане използването на вградения сесиен механизъм.

Ако само първият е активиран, тогава в началото на сесията (всеки разговор session_start()) се задава бисквитка за клиента. Браузърът правилно връща тази бисквитка с всяка следваща заявка и PHP има идентификатор на сесия. Проблемите започват, ако браузърът не връща бисквитки. В този случай, без да получи бисквитка с идентификатор, PHP винаги ще стартира нова сесия и механизмът няма да работи.

Ако само втората е активирана, бисквитката не е зададена. И това се случва, в името на което всъщност си струва да използвате вградения сесиен механизъм. След като скриптът свърши работата си и страницата е напълно оформена, PHP сканира цялата страница и добавя идентификатор на сесия към всяка връзка и към всеки формуляр. Изглежда нещо подобно:
Индекспревръща се в
Индекс
и към формулярите се добавя скрито поле

И браузърът, когато щракнете върху която и да е връзка или когато щракнете върху бутон във формата, ще изпрати в заявката променливата, от която се нуждаем - идентификатора на сесията!
По очевидни причини идентификаторът се добавя само към относителните връзки.

Теоретично, в нашите домашни сесии за бисквитки и базата данни, можете ръчно да зададете прехвърляне на ID на всички връзки - и след това нашите собствени сесиище работи независимо от бисквитките. Но ще се съгласите ли - по-приятно е, когато някой друг върши тази работа? ;-)

По подразбиране в най-новите версии PHP има и двете опции активирани. Как PHP се справя с това? Кук винаги е изложен. И връзките се допълват автоматично само ако PHP не е открил бисквитка с идентификатор на сесия. Когато потребител посети сайта за първи път по време на тази сесия, се поставя бисквитка и връзките се допълват. При следваща заявка, ако бисквитките се поддържат, PHP вижда бисквитката и спира попълването на връзки. Ако бисквитките не работят, тогава PHP продължава да добавя правилно идентификатор към връзките и сесията не се губи.
Потребителите с активирани бисквитки ще видят дългата връзка с идентификатора само веднъж.

уф. Прехвърлянето на ID е завършено.
Сега всичко, което остава, е да свържете файла с данни към него от страна на сървъра.
PHP ще направи това вместо нас. Достатъчно е само да пишете
session_start();
$_SESSION [ "test" ]= "Здравей свят!" ;

И PHP ще запише тестовата променлива във файла, свързан с тази сесия.
Тук има една много важна забележка.
Масив $_SESSION- специален.
Всъщност той съдържа променливите, които искаме да направим достъпни в различни скриптове.
За да поставите променлива в сесия, просто я присвоете на елемента от масива $_SESSION.
За да получите стойността му, просто осъществете достъп до същия елемент. Пример ще бъде даден по-долу.

PHP също се справя със събирането на боклука - премахване на остарели файлове. Както и кодиране на данни и куп други необходими неща. В резултат на тази грижа работата със сесии е много проста.
Тук всъщност стигаме до примера за това как работят сесиите.
Много малък пример:
session_start();

ехо „Актуализирахте тази страница“. $_SESSION["брояч"]++. " веднъж. " ;
ехо "
актуализация";
?>

Проверяваме дали имаме променлива брояч в сесията; ако не, тогава я създаваме със стойност 0, след което показваме нейната стойност и я увеличаваме с единица. Увеличената стойност ще бъде записана в сесията и при следващото извикване на скрипта променливата ще има стойност 1 и т.н.
Всичко е много просто.

За да имате достъп до сесийните променливи на която и да е страница от сайта, трябва да напишете САМО ЕДИН (!) ред в самото начало на ВСЕКИ файл, в който се нуждаем от сесии:
session_start();
И след това достъп до елементите на масива $_SESSION. Например проверката за оторизация би изглеждала така:
session_start();
if ($_SESSION [ "упълномощен" ]<> 1 ) {
header("Местоположение: /auth.php" );
изход;
}

Премахване на променливи от сесия.
Ако имате register_globals=off, тогава просто пишете
unset($_SESSION [ "var" ]);
Ако не, тогава близо доТрябва да пиша с нея
session_unregister("var");

Най-честите грешки, които PHP създава при опит за работа със сесии, са следните:
Два от тях
Предупреждение: Не може да се изпрати бисквитка на сесията - заглавките вече са изпратени
Предупреждение: Не може да се изпрати ограничител на кеша на сесията - заглавките вече са изпратени

поради същата причина, решението е описано в тази тема
трето,
Предупреждение: неуспешно отваряне (/tmp\sess_SID, O_RDWR): Няма такъв файл или директория (2) в full_script_path на номер на ред(преди тя изглеждаше така Предупреждение: Неуспешно записване на данни за сесията (файлове). Моля, проверете дали текущата настройка на session.save_path е правилна (/tmp)),
ако е преведено от английски, то обяснява подробно проблема: пътят до директорията, посочена в php.ini, където са записани сесийните файлове, не е наличен. Тази грешка е най-лесна за коригиране. Просто регистрирайте директория, която съществува и може да се записва, например,
session.save_path = c:\windows\temp
И не забравяйте да рестартирате Apache след това.

Както се оказа, човешкият интелект няма граници и затова съм принуден да обясня:
съобщение за третата грешка (директорията не може да бъде намерена) НЕИЗБЕЖНО ще доведе до появата на първите две, тъй като съобщението за грешка се извежда в браузъра и заглавките след него не могат да бъдат използвани. Затова не бързайте да търсите преждевременно заключение, а първо запишете верния път!

Следващият най-често срещан проблем при работа със сесии е тежкото наследство на register_globals. НЕ давайте имена на променливи на скрипта, които съответстват на индексите на масива $_SESSION!
С register_globals=on стойностите ще се презаписват взаимно и ще се объркате.
И ако register_globals=off, ще се появи друга грешка: „Вашият скрипт вероятно разчита на страничен ефект от сесията, който е съществувал до PHP 4.2.3.“, ако скриптът има сесийна променлива, която няма стойност, и глобална променлива с същото име . За да се отървете от него, винаги трябва да инициализирате променливи преди употреба (или поне да проверявате за съществуване) и да не давате имена на глобални променливи, които съвпадат с индексите на масива $_SESSION.

Ако не работи, но не се показват съобщения, добавете два реда в самото начало на скрипта, отговорен за показването на ВСИЧКИ грешки на екрана - напълно възможно е да има грешки, но просто не ги виждате.
ini_set("display_errors", 1);
съобщаване на грешка (E_ALL);

или вижте грешките в error_log. Като цяло темата за показване на съобщения за грешки е извън обхвата на тази статия, така че просто се уверете, че можете поне да ги видите. Можете да прочетете малко повече за намирането на грешки в този раздел.

Ако сте сигурни, че няма грешки, но даденият пример така или иначе не работи, тогава може би PHP не позволява предаване на id чрез URL, и бисквитките не работят по някаква причина.
Вижте какво не е наред с вашите бисквитки.
Като цяло, ако вашите сесии не работят, тогава първо опитайте да предадете идентификатора на сесията ръчно, тоест направете връзка и присвоете идентификатор към нея:
session_start();
if (!isset($_SESSION [ "брояч" ])) $_SESSION [ "брояч" ]= 0;
ехо „Актуализирахте тази страница“. $_SESSION["брояч"]++. " веднъж.

актуализация";
?>

Трябва обаче да се уверите, че директивата session.use_only_cookies не е активирана, което не позволява на PHP да приеме идентификатора на сесията, ако е бил предаден чрез URL

Ако този пример не работи, тогава проблемът е или в тривиален правописни грешки(половината от "проблемите" със сесиите идват от грешно изписано име на променлива), или също Стара версия PHP: поддръжката на сесии се появи във версия 4.0 и масивът $_SESSION- в 4.1 (Преди това се използваше $HTTP_SESSION_VARS).
Ако работи, значи проблемът е в бисквитките. Следете какъв вид бисквитка сървърът задава на браузъра и дали браузърът го връща. Много е полезно да търсите, като разглеждате обмена на HTTP заглавки между браузъра и сървъра.
Обяснението как работят бисквитките е извън обхвата на този вече твърде дълъг текст, но поне се уверете, че сървърът изпраща бисквитка с идентификатор, а браузърът я връща. И в същото време идентификаторите съвпадат един с друг =)
Настройката на бисквитката трябва да изглежда така
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6;
или как
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; път=/
(ако заявявате скрипта не от основната директория)
Отговорът на сървъра трябва да изглежда така
Бисквитка: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6
или
Бисквитка: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b
ако браузърът връща бисквитки, различни от ID на сесията.

Ако браузърът не връща бисквитки, проверете дали бисквитките изобщо работят.
Уверете се, че домейнът, до който осъществявате достъп, има нормално име (с поне една точка и без незаконни знаци като долни черти) и изчистете кеша на браузъра си - това са две основни причини бисквитките да не работят.

Ако примерът от тук работи, но вашият собствен код не, тогава проблемът очевидно не е в сесиите, а в алгоритъма. Потърсете къде сте загубили променливата, прехвърлете примера оттук стъпка по стъпка и отстранете грешките във вашия скрипт.

Друг проблем може да възникне, ако използвате пренасочване на заглавки или JavaScript навигация.
Факт е, че PHP автоматично добавя идентификатора на сесията само към връзки като , но не прави това за заглавки, JavaScript, мета тагове.
Следователно трябва да добавите идентификатора ръчно, например по следния начин:
header("Местоположение: /script.php?" . session_name(). "=" . session_id());

Също така, много рядък проблем и е напълно неясно откъде идва е, че настройката session.save_handler има стойност, различна от файловете. Ако това не е така, коригирайте го.

Безопасност
Сигурността на сесията е обширна тема. Затова ще се спра на няколко основни момента.
Най-учебническият е да не прекарвате идентификатора през адресната лента. Това е написано дори в php.ini, но това ограничава функционалността на сесиите. Ако решите да следвате този съвет, тогава в допълнение към session.use_trans_sid = 0, не забравяйте session.use_only_cookies = 1
Препоръчително е да обвържете сесията с IP адрес: по този начин, ако идентификаторът бъде откраднат, злодеят пак няма да може да го използва в повечето случаи.
Препоръчително е да използвате директивата session.save_path, която ви позволява да зададете своя собствена директория за запазване на сесийните файлове. Това е по-сигурно, отколкото да ги съхранявате в споделената временна директория на сървъра по подразбиране.

Допълнителна информация:

  • В допълнение към бисквитките, механизмът на сесията също изпраща заглавки, които забраняват кеширането на страницата (същия ограничител на кеша). За html това е правилно и необходимо. Но когато се опитате да изпратите файл с помощта на скрипт, който проверява авторизацията, Internet Explorer отказва да го изтегли. Това е заради това заглавие. Обадете се
    session_cache_limiter("частен");
    трябва да реши проблема, преди да започне сесията.
  • Колкото и странно да изглежда, но в масива $_SESSIONНе можете да използвате цифрови индекси - $_SESSION [ 1 ], $_SESSION [ "10" ]- сесиите няма да работят.
  • Някъде между версии 4.2 и 5.0 не беше възможно да се зададе session.use_trans_sid с помощта на ini_set(). Започвайки от 5.0 вече е възможно отново.
  • Преди версия 4.3.3 на бисквитката, PHP изпращаше бисквитка само ако нямаше идентификатор в заявката при стартиране на сесията. Сега при всяко обаждане се изпраща бисквитка session_start()

    Пример за оторизация чрез сесии
    Нека илюстрираме всичко по-горе с малък пример:
    Нека създадем файла auth.php:
    if (isset($_POST [ "auth_name" ]))
    {
    $sql = "SELECT * FROM потребители WHERE name=?s";
    $row = $db -> getRow($sql, $_POST["auth_name"]);
    if ($row && password_verify ($_POST [ "auth_pass" ], $row [ "pass" ])) (
    $_SESSION [ "user_id" ] = $row [ "id" ];
    }
    заглавие ("Местоположение: http://" . $_SERVER [ "HTTP_HOST" ]. $_SERVER [ "REQUEST_URI" ]);
    изход;
    }

    if (isset($_GET [ "действие" ]) И $_GET [ "действие" ]== "излизане" ) (
    session_start();
    session_destroy();
    header("Местоположение: http://" . $_SERVER [ "HTTP_HOST" ]. "/" );
    изход;
    }

    if (!isset($_SESSION [ "user_id" ])) (
    ?>








    изход;
    }

    Сега всичко, което трябва да направите, е да напишете реда във всички защитени скриптове
    изискват "auth.php" ;
    Този пример предполага, че сесията вече е започнала и е създадена връзка с базата данни с помощта на Class за безопасна и удобна работа с MySQL. Той също така предполага, че паролата е хеширана с помощта на препоръчаната функция password_hash.
    Пример за защитен файл:

    session_start();
    включват "safemysql.class.php" ;
    $db = нов safemysql ([ "db" => "тест" ]);
    включват "auth.php" ;
    ?>
    тайна

    излез от профила си

    УПС! Много полезни връзки:
    http://www.php.net/manual/ru/ref.session.php - най-новата и най-актуална информация за поддръжката на сесии в PHP в официалната документация, плюс множество потребителски коментари. Горещо препоръчително четене.
    http://phpclub.ru/manrus/f/ref.session.html - МНОГО остарял превод на тази глава на руски, от документацията, преведена от Александър Пирамидин.
    http://phpclub.ru/detail/article/sessions
    Статия с патетичното заглавие „Истината за сесиите“. Оставя амбивалентно впечатление. В началото авторът говори МНОГО ясно за механизма на сесията, но методите, които предлага в края на статията, са напълно неясни.

    Статия от учебника на Дмитрий Бородин от сайта
    http://php.spb.ru/ силно НЕ се препоръчва.
    Хора, ужасно е остаряло. Не само, че съдържа фактически неточности, но със сесии в PHP просто не се работи дълго време.
    Много благодаря на Дима за това, това беше първата статия за сесиите на руски, аз самият се учих от нея, но сега трябва да я изпратя в заслужена почивка.
    За съжаление, много други статии в интернет, които не са актуализирани от години, също са остарели.

  • Поздрави, скъпа общност.

    Първо, искам да ви благодаря за многото полезен ресурс. Тук неведнъж съм намирал много интересни идеи и практически съвети.

    Целта на тази статия е да подчертае клопките при използването на сесии в PHP. Разбира се, има документация за PHP и много примери и тази статия не претендира за такава пълно ръководство. Той е предназначен да разкрие някои от нюансите на работа със сесии и да защити разработчиците от ненужна загуба на време.

    Най-често срещаният пример за използване на сесии е, разбира се, оторизацията на потребителя. Нека започнем с най-елементарното изпълнение, за да го развиваме постепенно, когато възникнат нови задачи.

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

    Функция startSession() ( // Ако сесията вече е стартирана, спрете изпълнението и върнете TRUE // (параметърът session.auto_start във файла с настройки php.ini трябва да бъде деактивиран - стойността по подразбиране), ако (session_id()) върне true; else return session_start(); // Забележка: Преди версия 5.3.0 функцията session_start() връщаше TRUE, дори ако имаше грешка // Ако използвате версия, по-стара от 5.3.0, направете допълнителна проверка за session_id() // след извикване на функцията session_start() destroySession() ( if (session_id()) ( // Ако има активна сесия, изтрийте сесийните бисквитки, setcookie(session_name(), session_id(), time(. )-60*60*24); // и унищожи сесията session_unset();

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

    Дотук всичко е ясно. Въпросите започват, когато трябва да приложите контрол на неактивността на потребителя (време за изчакване на сесията), да позволите на множество потребители да работят едновременно в един браузър и също така да защитите сесиите от неоторизирана употреба. Това ще бъде обсъдено по-долу.

    Мониторинг на бездействието на потребителите с помощта на вградени PHP инструменти

    Първият въпрос, който често възниква сред разработчиците на всички видове конзоли за потребители, е автоматичното прекратяване на сесията в случай на бездействие от страна на потребителя. Няма нищо по-лесно от това да направите това с помощта на вградените възможности на PHP. (Тази опция не е особено надеждна или гъвкава, но ще я разгледаме за пълнота).

    Функция startSession() ( // Време за изчакване на неактивност на потребителя (в секунди) $sessionLifetime = 300; if (session_id()) връща true; // Задаване на живота на бисквитката ini_set("session.cookie_lifetime", $sessionLifetime); // Ако потребител зададено е време за изчакване при неактивност, задайте продължителността на сесията на сървъра // Забележка: За производствен сървър се препоръчва предварително да зададете тези параметри във файла 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).

    Когато се създаде нова сесия, файл, наречен sess_, се създава в директорията, зададена като директория за съхранение на сесии в параметъра за PHP настройки session.save_path , Където - идентификатор на сесията. След това във всяка заявка, в момента на стартиране на вече съществуваща сесия, PHP актуализира времето за модификация на този файл. Така във всяка следваща PHP заявка, чрез разликата между текущия час и времето на последната модификация на файла на сесията, може да определи дали сесията е активна или животът й вече е изтекъл. (Механизмът за изтриване на стари сесийни файлове е разгледан по-подробно в следващия раздел.)

    Забележка:Тук трябва да се отбележи, че параметърът session.gc_maxlifetime се прилага за всички сесии в рамките на един сървър (по-точно в рамките на един основен PHP процес). На практика това означава, че ако няколко сайта работят на сървъра и всеки от тях има свое собствено време за изчакване на неактивност на потребителя, тогава настройката на този параметър на един от сайтовете ще доведе до настройката му за други сайтове. Същото важи и за споделения хостинг. За да се избегне тази ситуация, се използват отделни директории за сесии за всеки сайт в рамките на същия сървър. Задаването на пътя към директорията на сесиите се извършва с помощта на параметъра session.save_path във файла с настройки php.ini или чрез извикване на функцията ini_set(). След това сесиите на всеки сайт ще се съхраняват в отделни директории и параметърът session.gc_maxlifetime, зададен на един от сайтовете, ще бъде валиден само за неговата сесия. Няма да разглеждаме подробно този случай, особено след като имаме по-гъвкава опция за наблюдение на бездействието на потребителите.

    Контролиране на бездействието на потребителя с помощта на променливи на сесията

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

    За да разрешим този проблем, ще изоставим използването на вградени PHP механизми и ще въведем няколко нови променливи на сесията, които ще ни позволят сами да контролираме времето на бездействие на потребителя.

    Функция 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(); else ( // Ако времето за изчакване все още не е настъпило, // и ако заявката дойде в резултат на активност на потребителя, // актуализирайте променливата lastactivity със стойността на текущото време, // като по този начин удължите времето на сесията с още секунди sessionLifetime if ($isUserActivity) $_SESSION["lastactivity"] =; $t; ) ) връща истина; )

    Нека да обобщим. При всяка заявка проверяваме дали е достигнат таймаутът от последната активност на потребителя до текущия момент и ако е достигнат, унищожаваме сесията и прекъсваме изпълнението на функцията, връщайки FALSE. Ако времето за изчакване не е достигнато и параметърът $isUserActivity със стойност TRUE се предава на функцията, актуализираме времето на последната активност на потребителя. Всичко, което трябва да направим, е да определим в извикващия скрипт дали заявката е резултат от потребителска активност и ако не е, да извикаме функцията startSession с параметъра $isUserActivity, зададен на FALSE.

    Актуализация от 2013-06-07
    Обработка на резултата от функцията sessionStart().

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

    Както можете да видите, функцията sessionStart може да върне FALSE в два случая. Или сесията не може да бъде стартирана поради някои вътрешни грешки на сървъра (например неправилни настройки на сесията в php.ini), или продължителността на сесията е изтекла. В първия случай трябва да пренасочи потребителя към страница с грешка, че има проблеми на сървъра и формуляр за връзка с поддръжката. Във втория случай трябва да прехвърлим потребителя във формата за вход и да покажем съответното съобщение в него, че сесията е изтекла. За да направим това, трябва да въведем кодове за грешки и да върнем съответния код вместо FALSE, а в извикващия метод да го проверим и да действаме съответно.

    Сега, дори ако все още съществува сесия на сървъра, тя ще бъде унищожена при първия достъп до нея, ако времето за изчакване на неактивност на потребителя е изтекло. И това ще се случи независимо от продължителността на сесията, зададена в глобалните PHP настройки.

    Забележка:Какво се случва, ако браузърът е затворен и бисквитката с името на сесията е автоматично унищожена? Заявката към сървъра при следващото отваряне на браузъра няма да съдържа сесийните бисквитки и сървърът няма да може да отвори сесията и да провери времето за изчакване на неактивност на потребителя. За нас това е еквивалентно на създаване на нова сесия и не засяга функционалността или сигурността по никакъв начин. Но възниква справедлив въпрос - кой тогава ще унищожи старата сесия, ако досега сме я унищожавали след изтичане на таймаута? Или сега ще виси завинаги в директорията на сесиите? За да изчистите стари сесии в PHP, има механизъм, наречен събиране на боклука. Той се изпълнява по време на следващата заявка към сървъра и изчиства всички стари сесии въз основа на последната дата на промяна на файловете на сесията. Но механизмът за събиране на отпадъци не стартира с всяка заявка към сървъра. Честотата (или по-скоро вероятността) за стартиране се определя от два параметъра за настройка session.gc_probability и session.gc_divisor. Резултатът от разделянето на първия параметър на втория е вероятността за стартиране на механизма за събиране на боклук. По този начин, за да може механизмът за изчистване на сесии да се стартира с всяка заявка към сървъра, тези параметри трябва да бъдат зададени на еднакви стойности, например „1“. Този подход гарантира чиста директория на сесиите, но очевидно е твърде скъп за сървъра. Следователно в производствените системи стойността по подразбиране на session.gc_divisor е зададена на 1000, което означава, че механизмът за събиране на отпадъци ще работи с вероятност от 1/1000. Ако експериментирате с тези настройки във вашия файл php.ini, може да забележите, че в случая, описан по-горе, когато браузърът се затвори и изчисти всичките си бисквитки, все още има оставени стари сесии в директорията на сесиите за известно време. Но това не трябва да ви притеснява, защото... както вече беше посочено, това по никакъв начин не засяга безопасността на нашия механизъм.

    Актуализация от 2013-06-07

    Предотвратяване на замразяване на скриптове поради заключване на файл на сесия

    Коментарите повдигнаха въпроса за замразяването на едновременно стартирани скриптове поради блокиране на файла на сесията (най-впечатляващата опция е дългата анкета).

    Като начало отбелязвам, че този проблем не зависи пряко от натоварването на сървъра или броя на потребителите. Разбира се от повече искания, толкова по-бавно се изпълняват скриптовете. Но това е косвена зависимост. Проблемът възниква само в рамките на една сесия, когато сървърът получава няколко заявки от името на един потребител (например една от тях е дълга анкета, а останалите са редовни заявки). Всяка заявка се опитва да получи достъп до същия файл на сесията и ако предишната заявка не е отключила файла, тогава следващата ще виси в очакване.

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

    В нашия пример това означава, че веднага след отваряне на сесия, проверка на нейния живот и съществуването на оторизиран потребител, трябва да прочетем и запазим всички допълнителни изисквани от приложениетосесийни променливи (ако има такива), след това затворете сесията, като използвате извикване на session_write_close() и продължете да изпълнявате скрипта, независимо дали е дълга анкета или обикновена заявка.

    Защита на сесиите от неоторизирана употреба

    Нека си представим ситуацията. Един от вашите потребители получава троянски кон, който ограбва бисквитките на браузъра (в които се съхранява нашата сесия) и го изпраща на посочения имейл. Нападателят получава бисквитката и я използва, за да подмени заявка от името на нашия упълномощен потребител. Сървърът успешно приема и обработва тази заявка, сякаш идва от оторизиран потребител. Ако не се приложи допълнителна проверка на IP адреса, такава атака ще доведе до успешно хакване на акаунта на потребителя с всички произтичащи от това последствия.

    Защо това беше възможно? Очевидно, тъй като името и идентификаторът на сесията винаги са едни и същи за целия живот на сесията и ако получите тези данни, можете лесно да изпращате заявки от името на друг потребител (разбира се, в рамките на живота на тази сесия). Това може да не е най-често срещаният тип атака, но теоретично изглежда доста осъществимо, особено като се има предвид, че такъв троян дори не се нуждае от администраторски права, за да ограби бисквитките на браузъра на потребителя.

    Как можете да се предпазите от атаки от този вид? Отново, очевидно, чрез ограничаване на живота на идентификатора на сесията и периодична промяна на идентификатора в рамките на същата сесия. Можем също така да променим името на сесията, като изтрием напълно старата и създадем нова сесия, като копираме всички променливи на сесията от старата в нея. Но това не засяга същността на подхода, така че за простота ще се ограничим само до идентификатора на сесията.

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

    (Ще пропуснем частта от кода, която вече беше обсъдена).

    Функция startSession($isUserActivity=true) ( ​​​​// Продължителност на живота на идентификатора на сесия $idLifetime = 60; ... if ($idLifetime) ( // Ако е зададена продължителност на живота на идентификатора на сесията, // проверете времето, изминало от сесията създадено или последното повторно генериране // (време на последната заявка, когато променливата за начало на сесията е актуализирана) if (isset($_SESSION["starttime"])) ( if ($t-$_SESSION["starttime"] >= $ idLifetime) ( // Времето, за което идентификаторът на сесията е изтекъл // Генериране на нов идентификатор session_regenerate_id(true); $_SESSION["starttime"] = $t) ) else ( // Стигаме дотук, ако сесията току-що е създадена // Задайте времето за генериране на идентификатора на сесията на текущо време$_SESSION["начален час"] = $t; ) ) връща истина; )

    Така че, когато създаваме нова сесия (което се случва, когато потребителят влезе успешно), задаваме променливата starttime на сесията, която съхранява за нас времето на последното поколение на идентификатора на сесията, на стойност, равна на текущото време на сървъра. След това във всяка заявка проверяваме дали е минало достатъчно време (idLifetime) от последното генериране на идентификатора и ако е така, генерираме нов. По този начин, ако по време на зададения живот на идентификатора атакуващият, получил бисквитката на оторизирания потребител, няма време да я използва, фалшивата заявка ще се счита от сървъра за неоторизирана и атакуващият ще бъде отведен до страницата за вход .

    Забележка:Новият идентификатор на сесията попада в бисквитката на браузъра, когато се извика функцията session_regenerate_id(), която изпраща новата бисквитка, подобно на функцията session_start(), така че не е необходимо да актуализираме бисквитката сами.

    Ако искаме да направим нашите сесии възможно най-сигурни, достатъчно е да зададем живота на идентификатора на едно или дори да премахнем функцията session_regenerate_id() от скобите и да премахнем всички проверки, което ще доведе до повторно генериране на идентификатора във всеки искане. (Не съм тествал въздействието на този подход върху производителността и мога само да кажа, че функцията session_regenerate_id(true) по същество изпълнява само 4 действия: генериране на нов идентификатор, създаване на заглавка със сесийната бисквитка, изтриване на старата и създаване нов файл на сесията).

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

    Възможност за едновременна работа в един браузър от името на няколко потребители

    Последната задача, която бих искал да разгледам, е възможността няколко потребители да работят едновременно в един браузър. Тази функция е особено полезна на етапа на тестване, когато трябва да емулирате едновременната работа на потребителите и е препоръчително да направите това в любимия си браузър, вместо да използвате целия наличен арсенал или да отваряте няколко копия на браузъра в режим инкогнито .

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

    Функция startSession($isUserActivity=true, $prefix=null) ( ... ако (session_id()) връща true; // Ако потребителският префикс е предаден в параметрите, // задайте уникално име на сесия, което включва това префикс, // в противен случай задайте общо име за всички потребители (например MYPROJECT) session_name("MYPROJECT".($prefix ? "_".$prefix: "")); if (! session_start()) return false; ... )

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

    Заключение

    В заключение ще предоставя пълния окончателен код на нашите функции за работа с PHP сесии, включително всички задачи, обсъдени по-горе.

    Функция startSession($isUserActivity=true, $prefix=null) ( $sessionLifetime = 300; $idLifetime = 60; if (session_id()) връща true; session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; if ($sessionLifetime) && $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; ) връщане true; функция destroySession() ( if (session_id()) ( session_unset(); setcookie(session_name(), session_id(), time() -60* 60*24); session_destroy();

    Надявам се, че тази статия ще спести малко време на тези, които никога не са се задълбочавали в механизма на сесиите, и ще даде достатъчно представа за този механизъм на тези, които тепърва започват да се запознават с PHP.