Dla niektórych wygasanie sesji jest zabezpieczeniem (banki, etc.). Realizując jeden z projektów oczekiwałem od systemu tego, aby użytkownik nigdy nie gubił sesji, gdy ma otwarte okno w przeglądarce. Dlaczego? Może dodaje posta, być może uzupełnia dość obszerny tekst na stronie. Gdy klika zapisz, przerzuca go do strony logowania, a cały tekst zniknął za sprawą tego, że jego przeglądarka nie zapisuje wartości pól formularza. Skąd to znamy.
Jak użytkownik gubi sesję?
- Jego ciastko wygasa, więc serwer nie może go zidentyfikować z sesją.
- Po jakimś czasie, choćby odtworzył ciastko, plik sesji znika z naszego serwera (garbage collection).
Rozwiązania:
- Wydłużenie czasu wygasania ciastka i sesji.
- Odświeżenie strony w interwale mniejszym, niż wynosi czas wygasania sesji i ciastka.
Rozmyślając nad podtrzymaniem sesji, próbowałem znaleźć wszystkie metody oraz wybrać najlepszą. Wszystkie sprowadzają się do „odświeżenia” strony lub jej fragmentu tak, aby nasz silnik wykonał tylko potrzebne session_start(); czyli podtrzymanie aktywności sesji. Jest kilka mniej lub bardziej zadowalających sposobów:
- Odświeżenie całej strony.
To może spowodować, że dane wprowadzane przez użytkownika w formularzu zostaną utracone. Ponad to, jeżeli użytkownik czyta newsy, denerwującym może być fakt, że lista nagle zostanie przescrollowana do góry (prócz opery). - Wysłanie requestu ajax w tle.
Minusem jest to, że trzeba używać biblioteki ajax lub pisać dodatkowy kod javascriptu. Jeżeli ktoś na stronie używa jakiegoś ajaxa – co za różnica. Poza tym same plusy. - Odświeżanie ukrytej ramki iframe lub elementu frameset.
Minusów usablity prawie brak. Brak potrzeby instalacji javascriptów i ajaxa. Odświeżacz powinien wysłać nagłówek Refresh lub odpowiedni metatag.
Sposób 3 wydaje mi się najlepszy. Można go ulepszyć w ten sposób, aby ramka nie wysyłała żądania zaraz po załadowaniu strony. Powodowałoby to podwójne requesty do serwera.
- Przykład z ukrytym iframe.
- Przykład z Advajax.
- plik
ping.php
wygląda wówczas następująco: aplikacje nie używające frameworków ingerujących w standardowe działanie sesji
session_start(); header('Refresh: 60');
Zapewne znajdą się osoby, które powiedzą: a co z użytkownikami, którzy mają wyłączone ramki, lub ich przeglądarki w ogóle ich nie obsługują. Zapytam wówczas: a co z użytkownikami, którzy nie akceptują cisteczek (wówczas sesje nie są dla nich użyteczne, chyba, że użyjemy przesłyki jej identyfikatora w adresie url). Dopytam również: a co z użytkownikami, którzy mają wyłączony Javascript? Patologiczne przepadki się po prostu pomija 😉
Widzę że zaczałeś regularnie blogować, i to z dosyc dobrym skutkiem – tematy b.ciekawe.
Mam nadzieje że nadal będziesz kontynuował.
Czekam na następny wpis o PHP;)
Pozdrawiam.
Pomimo, że napisałeś, że można się czepiać – to jako przykład podam siebie – używam ff z noscriptem – blokuje wszystkie JS, ewentualnie gdy potrzebuję – mogę je odblokować, ale domyślnie blokuję też ramki, a odblokowuje je jedynie wtedy gdy je widzę i akurat tego potrzebuję – tym sposobem JS jak najbardziej spełni swoje zadanie, a ukryta ramka nie.
Nie wiem czemu zrezygnowałeś tak łatwo z JS – własna funkcja do ajaxa to parenaście linijek które się po prostu kopiuje np. z poprzedniego projektu, ewentualnie można użyć biblioteki – linijek jeszcze mniej.
Bezpieczeństwo – głównym powodem dla którego sesje wygasają jest to żeby ktoś niepowołany nie dostał dostępu do strony – jeśli chcesz by użytkownik mógł dłużej działać to może po prostu zamiast na godzinę ustawić długość sesji na dzień.
Zaraz mi ktoś powie, że jeśli by użyć JS to przecież on robi to samo co iframe, owszem, dlatego też zwykłe regenerowanie ciasteczek mija się z celem, ale jeśli wziąć pod uwagę to że jest to JS, to możemy zauważyć że skrypt który sprawdzi czy coś się np. textarea zmieniło w przeciągu ostatniej minuty i wtedy ewentualnie via http poprosić o zregenerowanie ciasteczka, w przeciwnym razie pozwolić sesji umrzeć.
@Istalacar, dzięki za wypowiedź. Co do wydłużenia czasu żywotności ciasteczka, a zarazem plików na serwerze – wolałbym tego uniknąć. Przyczyny są jasne tylko w przypadku większej liczby userów on-line lub portalu, który ma służyć: wejdź, zobacz, wyjdź. Wówczas kumulacja plików na serwerze byłaby dość niebezpieczna.
Jeżeli nie akceptujesz iframe, co za problem połączyć oba sposoby. I to nie jest głupi pomysł, chyba zastosuję, sprawdzę jak się przyjmuje i ewentualnie opublikuję.
Odnośnie monitorowania formularzy – problem gubienia się sesji nie tyczy się tylko formularzy, ale statycznego przeglądania strony również. Dlatego wolałbym na sztywno wysłać żądanie http o przedłużenie ciasteczka.
Póki co w poście pojawił się przykład z advajax.
Podajesz przykład z formularzem. Rozwiązanie jest jeszcze prostsze niż to, co napisałeś. Dodatkowe pole HIDDEN z wartością identyfikatora sesji – gdy wygasa nam ciasteczko.
A co z garbage colletion? Prosta sprawa. Nie usuwam sesji młodszych niż 24 godziny, a czasem – gdy sesja powinna być aktywna np. 3-7 dni – nawet dłużej. Aktywność sesji to zupełnie inna sprawa. Poza tym nie widzę problemu, gdy nagle wygasa mi sesja, a ja byłem w trakcie pisania czegoś i chcę to teraz opublikować. Pojawia mi się formularz logowania, wypełniam, a dane i tak lecą np. do bazy danych. Aczkolwiek sposób z umieszczeniem identyfikatora sesji w formularzu jest najlepszy, tak przynajmniej uważam. Cookie może wygasnąć, ale aplikacja wie, że warto sprawdzić pola formularza – może tam ID siedzi. Dodatkowo strona, z której przyszedł, musi się zgadzać z tym, co ustawię.
Przekazywanie SID przez adres dodatkowo upraszcza sprawę. Wróć do zdania o aktywności i przetrzymywaniu danych sesyjnych, to zrozumiesz.
Tak, ale przesyłanie SID w URI jest niebezpieczne ze względu na stosunkowo łatwą możliwość skradnięcia sesji. Co prawda pola HIDDEN również można dołożyć/zmienić ale trzeba o nich wiedzieć, a dodatkowo wiedzieć, że np. mają działać tylko po wygaśnięciu ciasteczka. Dużo lepsze zabezpieczenie.
Sprawdziłem przykład z iframe. Nie zadziałał 🙁
Pierwsze co mi przyszło do głowy, to że sesja wywoływana przez główny skrypt ma inny identyfikator niż sesja wywoływana przez ping.php. Sprawdziłem to – wstawiłem do skryptu głównego echo z identyfikatorem sesji i do ping.php alert js też z identyfikatorem – były różne :/
Takie podtrzymanie sesji nie ma więc prawa funkcjonować.
Sprawdzałem przykład z iframe, ale podejrzewam, że z ajaxem będzie dokładnie tak samo, prawda?
Obie sesje winny być takie same w każdym przypadku. Masz jakiś problem z przesyłaniem Cookies lub PHPSESSID w adresie. Przykłady testowane, jednego z nich (ajaxa) używam praktycznie w każdym projekcie.
No rzeczywiście. Wcześniejsze próby przeprowadzałem na istniejącej aplikacji a teraz zrobiłem dwa gołe pliki dokładnie wg Twojego przykładu i oba identyfikatory były takie same.
Żeby hulało w tej aplikacji, o której wspomniałem, dodałem przekazywanie session_id do ping.php w URLu, przez GET (oIframe.src=’./ping.php?sesja=”.session_id().”’;), a w ping.php dodałem na samym początku ustawianie identyfikatora session_id($_GET[„sesja”]). Działa elegancko 🙂
Dzięki!
Dzięki za podzielenie się spostrzeżeniami na ten temat.
Jest jeszcze jeden sposób, cykliczne żądanie z poziomu JS o obrazek, gdzie skrypt php po wywołaniu session_start() tworzy obrazek 1×1 i wysyła go jako image/jpeg.
Jak dla mnie najbardziej eleganckie rozwiązanie.
Na początek – spodobało mi się dodawanie komentarza poprzez zaznaczanie box-a 🙂 Niezły sposób a implementowałem już wiele z często niezadowalającym skutkiem.
Czytam o Waszych różnych pomysłach/sposobach zabezpieczania przed niewygaśnięciem sesji. Testuję właśnie ten z ajaxem i zobaczę czy za godzinkę mnie nie wywali.
Myślałem, że to będzie proste jak z ciasteczkami – jedna linijka z informacją o długości 'żywotności’ sesji a tu takie zabiegi i propozycje (szczególnie ciekawa i niezrozumiała jest dla mnie propozycja eleganckiego rozwiązania mego przedmówcy).