[PHP] Sessies en garbage collection, lifetime + 1 request?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Booster
  • Registratie: Februari 2000
  • Laatst online: 13-09 14:23

Booster

Superuser

Topicstarter
Ik probeer een constructie te maken in PHP 5.1 waarbij session cookies gebruikt worden met een maximale levensduur van (bv) 10 minuten. Naar mijn idee zou de sessie met onderstaande code na 10 minuten niet meer moeten werken, echter in realiteit werkt de sessie voor 10 minuten, + 1 request.

Uitleg: na 10 minuten kan de gebruiker nog precies één pagina succesvol opvragen, en pas bij de pagina die hij daarna bezoekt wordt hij automatisch uitgelogd. Dit is keer op keer reproduceerbaar.

Dit specifieke probleem kan ik nergens terugvinden op het grote boze interwebz, maar ik vermoedde een probleem met garbage collection. Ik heb de instellingen daarvan nagekeken. De kans dat de garbage collector zijn werk doet is 1/1 (zoals hieronder ook aan de instellingen te zien is).

Ik heb mijn code nu al meerdere keren doorlopen op fouten, maar ik weet niet waar ik verder nog zou moeten zoeken.

Hieronder de relevante snippets in de volgorde waarin ze uitgevoerd worden:

De config die altijd als eerste wordt geladen:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
$site['protocol'] = empty($_SERVER['HTTPS']) ? 'http' : 'https';
$site['using_ssl'] = ($site['protocol'] === 'https') ? TRUE : FALSE;
$site['domain'] = 'service.x.nl';
$site['web_path'] = '/beheer/';
$site['url'] = $site['protocol'].'://'.$site['domain'].$site['web_path'];

$site['cookie']['name'] = 'mc_controlpanel';
$site['cookie']['lifetime'] = 10; // in minutes
$site['cookie']['httponly'] = TRUE;

ini_set('session.gc_maxlifetime', $site['cookie']['lifetime']*60);
ini_set('session.gc_divisor', '1');
ini_set('session.gc_probability', '1');


Bij het inloggen zet ik de sessie:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
session_name($site['cookie']['name']);

$session_started = session_start();
$cookie_set = setcookie(session_name(),
                        session_id(),
                        time()+$site['cookie']['lifetime']*60,
                        $site['web_path'],
                        $site['domain'],
                        $site['using_ssl'],
                        $site['cookie']['httponly']);

if ($session_started && $cookie_set) {
    // zetten van enkele $_SESSION vars en een redirect met header('Location: '.$site['web_path'].'start.php');
}


Daarna, bij iedere request voor een beschermde pagina:
PHP:
1
2
3
4
5
6
7
8
9
session_name($site['cookie']['name']);
$session_started = session_start();

if ($session_started) {
    // lezen enkele $_SESSION vars, indien OK: serveer beschermde pagina
} else {
    header('Location: '.$site['web_path'].'loguit.php');
    exit();
}


loguit.php:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
session_name($site['cookie']['name']);
session_start();

/*  Set lifetime for cookie to Jan 1, 1970, 8:00:01AM, so it'll get destroyed immediately.
    If expire is not set, cookie will remain in memory until browser is closed.
*/
$cookie_set = setcookie(session_name(),
                        session_id(),
                        1,
                        $site['web_path'],
                        $site['domain'],
                        $site['using_ssl'],
                        $site['cookie']['httponly']);

$session_destroyed = session_destroy();

if ($cookie_set && $session_destroyed) {
    // "u bent uitgelogd"
} else {
    // niet uitgelogd, foutafhandeling
}


Gezien de gc_divisor en gc_probability beide 1 zijn, is er iets mis met mijn denkwijze?

The cake is a lie | The Borealis awaits...


Acties:
  • 0 Henk 'm!

  • _Sunnyboy_
  • Registratie: Januari 2003
  • Laatst online: 22:39

_Sunnyboy_

Mooooooooooooooooo!

Session & handmatige cookies. Wat probeer je precies te doen? Begrijp je wel hoe sessies werken?

Normaal gesproken sla je sessie variabelen niet in een cookie op (= op de computer van de client) zoals jij maar in de sessie zelf (=op de server). Op de client staat dan alleen de sessie id. Wel zo veilig, kan niemand namelijk je sessie variabelen aanpassen door simpel de cookie te bewerken tussen twee pageviews.

Volgens mij heeft je probleem niks met garbage collect te maken, maar gewoon aan de volgorde waarop je je checks doet.

Eerst inloggen & uitloggen, dan kijken of de sessie timed out is en zoja de user uitloggen, daarna pas checken of je user nog steeds in gelogd is.

Onderstaande code doet volgens mij wat je wil
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php 
session_start(); 

if (<username ok & password ok>) { //pseudo code. gebruik je eigen login check
    $_SESSION['logged_in'] = true;
    $_SESSION'['userid'] = <id van user>; // etc 
    $_SESSION['timestamp'] = time();
}

if (<user wil uitloggen>) {
   session_destroy();
}

if (isset($_SESSION['timestamp']) && ($_SESSION['timestamp'] + <max sessie lifetime in seconden> < time())) {
   // sessie te oud. log user uit
   session_destroy();
}

// check of de user is ingelogd
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true)
{
   // user is ingelogd. Toon beveiligde data
}
else
{
  // user is niet ingelogd. Toon whatever
}
?>

Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life


Acties:
  • 0 Henk 'm!

  • hostname
  • Registratie: April 2009
  • Laatst online: 17-09 17:56
session_destroy logt je niet uit, maar stopt slechts het gebruik van sessies. Nog een keertje session_start() en je kan weer bij de variablen in $_SESSION.

Acties:
  • 0 Henk 'm!

  • Booster
  • Registratie: Februari 2000
  • Laatst online: 13-09 14:23

Booster

Superuser

Topicstarter
_Sunnyboy_ schreef op maandag 24 augustus 2009 @ 21:41:
Normaal gesproken sla je sessie variabelen niet in een cookie op (= op de computer van de client) zoals jij maar in de sessie zelf (=op de server). Op de client staat dan alleen de sessie id.
Volgensmij begrijp ik sessies prima.

Enige wat ik opsla in de cookie is de session_id. Ik zie dan ook niet waar ergens in mijn code jij denkt te zien dat ik $_SESSION variabelen in de cookie opsla? $_SESSION[] is immers voor de sessionfile op de server.

The cake is a lie | The Borealis awaits...


Acties:
  • 0 Henk 'm!

  • _Sunnyboy_
  • Registratie: Januari 2003
  • Laatst online: 22:39

_Sunnyboy_

Mooooooooooooooooo!

Booster schreef op maandag 24 augustus 2009 @ 21:53:
[...]

Volgensmij begrijp ik sessies prima.

Enige wat ik opsla in de cookie is de session_id. Ik zie dan ook niet waar ergens in mijn code jij denkt te zien dat ik $_SESSION variabelen in de cookie opsla? $_SESSION[] is immers voor de sessionfile op de server.
Ok ik moet ietsje beter lezen. :X Anyway dan sla je die sessie id dubbel op in een cookie. session_start() zorgt er namelijk al voor dat er een cookie met de sessie id wordt gemaakt :) Kan misschien geen kwaad maar nodig is het niet.

Hoe dan ook zou ik de expiratie check expliciet doen zoals in mijn code, en daarna pas checkt of de user nog steeds is ingelogd.
hostname schreef op maandag 24 augustus 2009 @ 21:51:
session_destroy logt je niet uit, maar stopt slechts het gebruik van sessies. Nog een keertje session_start() en je kan weer bij de variablen in $_SESSION.
Oeps je hebt gelijk.Even een unset($_SESSION) erbij dan maar :). De code die ik zelf gebruik is wel wat anders dan hier, maar het leek me simpeler als voorbeeld zo :)

Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life


Acties:
  • 0 Henk 'm!

  • Booster
  • Registratie: Februari 2000
  • Laatst online: 13-09 14:23

Booster

Superuser

Topicstarter
_Sunnyboy_ schreef op maandag 24 augustus 2009 @ 22:08:
Anyway dan sla je die sessie id dubbel op in een cookie. session_start() zorgt er namelijk al voor dat er een cookie met de sessie id wordt gemaakt :) Kan misschien geen kwaad maar nodig is het niet.
Riight. Ik zie nu pas het duplicaat cookie in mijn browser. Een met mijn eigen waardes en een met standaardwaardes.

Wat ik dacht: er wordt inderdaad automatisch een cookie aangemaakt, met standaardwaardes. Door zelf een cookie te maken met dezelfde naam vervang je deze standaardcookie door een met eigen instellingen. Het was zover ik wist dus geen duplicate cookie of iets dergelijks, maar ik dacht dat ik de cookie overschreef met de voor een met eigen preferences.

Mh. Ik vraag me nu wel af hoe je normaalgesproken dan de instellingen van de standaardcookie hoort te wijzigen. Daar zal ik zo naar kijken.
edit:
Dit moet dus niet met setcookie() maar met session_set_cookie_params(); als ik het goed zie
Hoe dan ook zou ik de expiratie check expliciet doen zoals in mijn code, en daarna pas checkt of de user nog steeds is ingelogd.
Dat is zeker een optie, echter ben ik wel benieuwd waarom mijn code niet werkt zoals verwacht.
hostname schreef op maandag 24 augustus 2009 @ 21:51:
session_destroy logt je niet uit, maar stopt slechts het gebruik van sessies. Nog een keertje session_start() en je kan weer bij de variablen in $_SESSION.
Klopt, maar dat gebeurt niet bij de uitlogcode. Ter info: De code om uit te loggen werkt perfect als deze eenmaal wordt aangeroepen. Dit is volgens de PHP-manual ook de "voorgeschreven" manier om een sessie te beëindigen. In het voorbeeld wordt geen unset() gebruikt of voorgesteld:
http://nl2.php.net/manual/en/function.session-destroy.php

The cake is a lie | The Borealis awaits...


Acties:
  • 0 Henk 'm!

  • iBasch
  • Registratie: Februari 2009
  • Laatst online: 17-09 22:11
Booster schreef op maandag 24 augustus 2009 @ 22:22:
Dat is zeker een optie, echter ben ik wel benieuwd waarom mijn code niet werkt zoals verwacht.
Je code werkt niet als verwacht omdat je niet bij iedere request checkt of de cookie weg is. De cookie expired in principe, niet de sessie. Ik snap alleen niet waarom hij na een request dan wel opeens weg is, maar goed. :/
Overigens is het in dit geval aan te raden om alleen maar sessions te gebruiken, geen combinatie van sessions en cookies... Zie de code van _Sunnyboy_.
Booster schreef op maandag 24 augustus 2009 @ 22:22:
Klopt, maar dat gebeurt niet bij de uitlogcode. Ter info: De code om uit te loggen werkt perfect als deze eenmaal wordt aangeroepen. Dit is volgens de PHP-manual ook de "voorgeschreven" manier om een sessie te beëindigen. In het voorbeeld wordt geen unset() gebruikt of voorgesteld:
http://nl2.php.net/manual/en/function.session-destroy.php
PHP:
1
2
unset($logged_in);
unset($_SESSION['logged_in']);


Zo gebruik ik hem meestal, dat zou moeten werken.

Acties:
  • 0 Henk 'm!

  • Booster
  • Registratie: Februari 2000
  • Laatst online: 13-09 14:23

Booster

Superuser

Topicstarter
iBasch schreef op maandag 24 augustus 2009 @ 22:48:
Overigens is het in dit geval aan te raden om alleen maar sessions te gebruiken, geen combinatie van sessions en cookies... Zie de code van _Sunnyboy_.
Agreed, ik dacht dat ik een session-cookie overschreef met eigen instellingen, maar dit blijkt nu echt een aparte cookie te zijn. Uiteraard niet mijn bedoeling.
PHP:
1
2
unset($logged_in);
unset($_SESSION['logged_in']);


Zo gebruik ik hem meestal, dat zou moeten werken.
Ik gebruik zelf ook een dergelijke sessie variabele ($_SESSION['autheticated']), die ik inderdaad als extra barriere eerst op FALSE zal zetten alvorens ik sessies unset, destroy, noem het maar op ;)

Mocht er op een ander niveau iets fout gaan, dan is authenticated in ieder geval FALSE.

Ik zal die fix, plus het gebruik van session_set_cookie_params() implementeren en daarna testen en hier terugrapporteren of dat dat wel het gewenste effect geeft.

[ Voor 8% gewijzigd door Booster op 24-08-2009 22:56 ]

The cake is a lie | The Borealis awaits...


Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 16-09 15:42

Sebazzz

3dp

Het is imo netter om gewoon de variabelen naar de beginstaat te zetten, en als je te lui bent om dat erin te scripten, in ieder geval dan $_SESSION = array() te doen.

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • Flying_Thunder
  • Registratie: December 2001
  • Niet online
Dit komt omdat middels session_start() de sessie data eerst wordt ingelezen, en de session gc pas daarna runt. Daarom kun je nog precies 1 request opvragen waarin het lijkt alsof de sessie nog actief is.
Dit scenario doet zich in jouw situatie alleen voor wanneer er geen andere pagerequests worden gedaan, zodat de gc geen kans heeft gehad om te runnen.

Zie ook http://bugs.php.net/bug.php?id=35602
Jammer weer van de typische reacties van PHP developers.

Ik zou trouwens eerder zelf een timestamp in de sessie zetten en a.d.h.v. daarvan bekijken of iemand nog ingelogd mag zijn, i.p.v. in elk request de gc te initieren.

Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 16-09 15:42

Sebazzz

3dp

The best solution in your case is to deploy a cron job that would run
periodically and remove all old sessions.
Het is ook niet te geloven hè :/

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • Booster
  • Registratie: Februari 2000
  • Laatst online: 13-09 14:23

Booster

Superuser

Topicstarter
Dank allen :) Ik ben er nog wat mee aan het stoeien maar het is hoe dan ook een verbetering :)

The cake is a lie | The Borealis awaits...


Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Das erg brak inderdaad van het PHP team. Wellicht is het toch een goed idee een eigen session handler te maken? Gewoon in de database een random (unieke) code zetten en die koppelen aan de user. Je houdt in de database de verlooptijd bij en zo kun je iemand na die 10min eruit gooien door simpelweg de sessie in de db te verwijderen.

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Het is gewoon een eenvoudige implementatie voor eenvoudige scripts welke geen last hebben van een naïeve garbage collector.

Zodra echt serieus met sessies gewerkt moet worden schrijft 9 vd 10 projecten een eigen handler. :) Ergens is het wel absurd dat iedereen het zelf doet, maar aan de andere kant heeft het ook weer een paar voordelen en heb je het in no-time in elkaar geklatst. :+

{signature}


Acties:
  • 0 Henk 'm!

  • Booster
  • Registratie: Februari 2000
  • Laatst online: 13-09 14:23

Booster

Superuser

Topicstarter
Inmiddels ben ik al een stuk verder en heb ik alles netjes in orde gemaakt om te werken met een eigen timestamp. Ook mijn idee van hoe de samenwerking met de PHP garbage collector zou moeten zijn is wat beter geworden. Hierdoor heb ik de regels om de garbage collector bij iedere page request te laten draaien kunnen schrappen.

Bij het inloggen wordt er een session geopened, de session cookie bij de client heeft geen vastgestelde lifetime ("expires at end of session"). Doormiddel van de timestamp wordt bepaald of de sessie nog geldig is voor de applicatie, en zoniet, dan worden alle session-variabele die erop wijzen dat iemand geauthenticeerd is weggehaald of op FALSE gezet.

Als iemand niet uitlogt dan maakt het niet echt uit dat zijn browser het sessie-ID nog weet, mijn applicatie weet bij een nieuwe request toch of de sessie nog geldig zou moeten zijn of niet. Hiermee maakt het ook niet echt meer uit wanneer de garbage collection precies gaat draaien, als dit maar niet is voordat de sessie volgens mijn applicatie kan verlopen. Ik gebruik hierbij overigens wel een aantal kleine mechanismen om session hijacking te voorkomen.

Een laatste probleempje ondervind ik als ik mensen zelf wil laten uitloggen. Het liefst zou ik dan de session cookie op de client direct laten verwijderen. Maar als ik in Firefox mijn cookies bekijk, dan staat de session cookie er ook nog nadat ik uitgelogd ben. Ook is de lifetime niet veranderd, deze is nogsteeds "expires at end of session".

Als ik daarna opnieuw in wil loggen dan wordt er wel netjes een nieuw session_id aangemaakt overigens.
Maar ik had verwacht dat de session cookie bij de client zou verdwijnen, dit is niet het geval. Hoort dat zo :?

Dit is de bijbehorende code:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* Set session cookie parameters */
session_set_cookie_params(0, $site['web_path'], $site['domain'], $site['using_ssl'], $site['cookie']['httponly']);

/* Open the session */
session_name($site['cookie']['name']);
session_start();

/*  If we inserted a message in the session then display it here. */
if ($_SESSION['logout_message'])
    print $_SESSION['logout_message'];

/* Remove values from the session, thereby resetting it. */
unset($_SESSION);

/*  Set lifetime for cookie to Jan 1, 1970, 8:00:01AM, so it'll get destroyed immediately.
    If expire is not set, cookie will remain in memory until browser is closed.
*/
$cookie_set = setcookie(session_name(),
              '',
              1,
              $site['web_path'],
              $site['domain'],
              $site['using_ssl'],
              $site['cookie']['httponly']);

$session_destroyed = session_destroy();

if ($cookie_set && $session_destroyed)
    print '<p class="succes">U bent uitgelogd.</p>';
else
    print '<p class="warning">Niet succesvol uitgelogd.</p>';

De session_destroy() die ik aanroep is gedeeltelijk overgenomen uit het artikel over session_destroy() op php.net.

Zowel $cookie_set als $session_destroyed geven TRUE, dus volgens PHP is het goed gegaan.

Daarop heb ik de headers die op en neer gaan gecontroleerd en daarin komt alles netjes over zo lijkt het:
code:
1
2
3
4
5
6
7
Set-Cookie:
mc_controlpanel=619de3c7b6a3a753f568a35acccd9823;
expires=Thu, 01-Jan-1970 00:00:01 GMT;
path=/beheer/;
domain=service.x.nl;
secure;
httponly


Als dit probleem ook opgelost is dan is het wat mij betreft klaar om gebruikt te gaan worden :)

The cake is a lie | The Borealis awaits...


Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Is de session_name() in beiden wel hetzelfde? Ik zou overigens de naam van de cookie zelf bepalen en dan in de value je sessieid zetten. Voor het verwijderen van de cookie kun je proberen om de tijd te zetten op "time() - 3600", de "1" zou volgens specs ook moeten werken maar met PHP weet je t nooit natuurlijk...

Acties:
  • 0 Henk 'm!

  • Booster
  • Registratie: Februari 2000
  • Laatst online: 13-09 14:23

Booster

Superuser

Topicstarter
Cartman! schreef op maandag 31 augustus 2009 @ 09:26:
Is de session_name() in beiden wel hetzelfde? Ik zou overigens de naam van de cookie zelf bepalen en dan in de value je sessieid zetten.
session_name() is overal hetzelfde. Ik kan ook zien dat de juiste cookie vermeld wordt in de headers, dus dit is zeker in orde :) De naam van de cookie heb ik zelf bepaald in de configuratie (helemaal bovenaan het topic).
Session-ID als value, dit had ik eerst, maar dat werkte niet. Daarom heb ik de lege session-ID maar overgenomen uit het PHP-voorbeeld.
Voor het verwijderen van de cookie kun je proberen om de tijd te zetten op "time() - 3600", de "1" zou volgens specs ook moeten werken maar met PHP weet je t nooit natuurlijk...
Daarom heb ik de headers aan de kant van de browser gecontroleerd, en daar komt de datum aan op de juiste manier. Het lijkt me dat PHP de waardes dus opzich in orde heeft.

The cake is a lie | The Borealis awaits...

Pagina: 1