Aktiva Sessioner. Sessioner

10.08.2022

En session (från latin - session - möte, engelska - session) är en tidsperiod som täcker användarens arbete på Internet från det ögonblick den första länken öppnas till den sista. Beräknas som tidsskillnaden mellan den första och den sista begäran. Det kan dock ta en annan tid innan den sista sidan visas av användaren, vilket gör det svårare att mäta tiden mellan två förfrågningar.

Hur är en session relaterad till HTTP-protokollet och COOKIES?

Vad en session är kan förklaras utifrån HTTP-protokollet. I sig självt har detta protokoll inte ett sätt att spara tillstånd mellan två operationer. Det vill säga, enkelt uttryckt, genom att öppna en sida och sedan flytta från den till en annan kommer HTTP inte att kunna fastställa att båda förfrågningarna tillhör samma användare. Och här kommer en speciell spårningsmetod till undsättning - sessionshantering (våra sessioner).
Som svar på frågan om vad en session är, kan vi därför säga att det är ett logiskt hjälpobjekt som underlättar överföringen av data mellan successiva HTTP-förfrågningar från en användare.
Cookies, som sessioner, lagrar information om användaren när han navigerar i olika sidor och förbättra protokollets prestanda. Men till skillnad från den andra, där data lagras i temporära filer på servern, sparar de den på användarens dator i form av små fragment.

Vad är sessioner till för?

Användningen av sessioner blir oumbärlig när man arbetar med webbplatser som forum, anslagstavlor och onlinebutiker, eftersom det i det här fallet är nödvändigt att spara data om användaren över flera sidor.

Sessionsstadier

Hela sessionen kan delas in i tre steg:

  • sessionsöppning (när användaren börjar arbeta med en specifik webbplats),
  • redovisning av sessionsvariabler (när du går till olika sidor),
  • slutet av sessionen.

På grund av det faktum att sessionsdata lagras på tredje parts server, då är det bäst att inte lagra stora mängder information i dem, utan att använda cookies.

För att visa den här sidan korrekt behöver du en webbläsare med JavaScript-stöd.

Användarbehörighetssessioner

För att komma åt den måste du följa länken Aktiva användare I kapitel Administrering - Hemsida i blocket Användare(Fig. 2); länk Aktiva användare I kapitel Administration – System – Licenser i kö Konkurrenskraftiga licenser(Fig. 3) - detta alternativ är endast möjligt om ELMA-systemet användertyp av konkurrensutsatta licenser ; trycka på en knapp Aktiva användare I kapitel Administration - Användare(Fig. 4).

Ris. 2. Avsnittet "Administration - Användare". Knappen Aktiva användare

Ris. 3. Avsnittet "Administration - System - Licenser". Länk till aktiva användare

Ris. 4. Avsnittet "Administration - Hemsida". Länk till aktiva användare

Användarnamn– detta fält innehåller fullständigt namn. användare i ELMA-systemet. Knappen bredvid användarnamnet låter dig avbryta (logga ut motsvarande användare) och ta bort information om alla sessioner för denna användare från listan.

IP-adress– detta fält innehåller IP-adressen för användaren från vilken auktoriseringen i systemet utfördes. Om ELMA webbfarm används för arbete, kommer deras riktiga IP-adresser att visas för varje aktiv användare.

Knappen bredvid IP-adressen låter dig avsluta motsvarande session och ta bort information om den från listan.

Ikonen bredvid IP-adressen betyder att den aktuella användaren för närvarande är auktoriserad i systemet. Efter att en viss tid har förflutit sedan det senaste användarsvaret avbryts den aktiva sessionen. Pausade sessioner markeras med en ikon. Timeoutperioden efter vilken den aktiva sessionen avbryts definieras i avsnittet Administration – System – Systeminställningar – Säkerhetsinställningar.

Senaste svar– detta fält innehåller information om den senaste åtgärden som utfördes av användaren i systemet eller om den senaste begäran som skickades från sidan till servern (en gång i minuten skickas en webbförfrågan från sidan som öppnas i webbläsaren till ELMA-servern till kontrollera anslutningen och samla in statistik).

Senaste användaråtgärd– detta fält innehåller information om den senaste åtgärden som utfördes av användaren i systemet i formatet Datum – Tid – Länk till den senaste sidan som användaren besökte. Denna länk är relativ. För att få fullständig länk du måste lägga till serveradressen till den (fig. 5).

Ris. 5. Bildande av den fullständiga adressen för länken till den senaste sidan som användaren besökte

Om det inte finns någon användaraktivitet under en viss tid, avslutas den aktuella sessionen automatiskt. Inställningar för sessionslagringslängd konfigureras i avsnittet

Många användare uppskattar den sömlösa synkroniseringen mellan enheter i Telegram. På grund av det faktum att inbyggda applikationer av denna plattformsoberoende messenger är installerade på alla operativsystem(dator och mobil) kan människor logga in på sina konton från flera enheter samtidigt.

I en nyligen uppdaterad Telegram-uppdatering skapades, förutom länkförhandsvisningar, ett nytt avsnitt i Telegrams säkerhets- och sekretessinställningar. Det heter "Active Sessions".

Avsnittet innehåller information om Telegram-användarens IP-adress och aktiva sessioner. Alla har möjlighet att avsluta oanvända sessioner, såväl som de aktiva sessioner som verkar misstänkta.

Auktorisation i två steg

En annan innovation rör också avsnittet säkerhet och integritet. Detta är auktorisering i Telegram i två steg (tvåstegsauktorisering).

Det låter dig skapa ett lösenord som (utöver verifiering via SMS) kommer att begäras varje gång du loggar in från en ny enhet.

Men du måste vara försiktig med detta: om det skapade lösenordet går förlorat, ägaren konto kommer inte att kunna logga in på sitt eget Telegram-konto från en annan gadget.

  • konfigurera möjligheten att återställa ditt lösenord via e-post;

Webbservern upprätthåller inte en permanent anslutning till klienten, och varje begäran behandlas som en ny, utan någon koppling till de tidigare.
Det vill säga, du kan varken spåra förfrågningar från samma besökare eller spara variabler för honom mellan vyerna enskilda sidor. Det var för att lösa dessa två problem som sessioner uppfanns.
Egentligen är sessioner, i ett nötskal, en mekanism som låter dig identifiera en webbläsare unikt och skapar en fil för denna webbläsare på servern där sessionsvariabler lagras.

Jag kommer inte att beskriva i detalj behovet av en sådan mekanism. Det handlar om läroboksfall som en kundvagn i en e-butik, auktorisering, samt inte helt triviala problem, som att skydda interaktiva delar av en webbplats från spam.

I princip är det ganska enkelt att göra din egen analog av sessioner, inte lika funktionell som den som är inbyggd i PHP, men liknande i huvudsak. På cookies och databas.
När vi begär ett skript ser vi efter om en cookie med ett specifikt namn har tagits emot. Om det inte finns någon cookie, ställ in den och skriv den till databasen ny linje med användardata. Om det finns en cookie läser vi data från databasen. Med en annan begäran tar vi bort gamla poster från databasen och nu har vi en sessionsmekanism redo. Det är inte alls svårt. Men det finns några nyanser som gör det att föredra att använda den inbyggda sessionsmekanismen.

Om endast den första är aktiverad, då i början av sessionen (varje samtal session_start()) en cookie ställs in för klienten. Webbläsaren returnerar denna cookie korrekt vid varje efterföljande begäran och PHP har en sessionsidentifierare. Problem börjar om webbläsaren inte returnerar cookies. I det här fallet, utan att ta emot en cookie med en identifierare, kommer PHP alltid att starta en ny session och mekanismen kommer inte att fungera.

Om endast den andra är aktiverad, är inte cookien inställd. Och det här är vad som händer, för vars skull det faktiskt är värt att använda den inbyggda sessionsmekanismen. Efter att skriptet har gjort sitt jobb och sidan är helt formaterad, skannar PHP hela sidan och lägger till en sessionsidentifierare till varje länk och till varje formulär. Det ser ut ungefär så här:
Index förvandlas till
Index
och ett dolt fält läggs till i formulären

Och webbläsaren, när du klickar på någon länk, eller när du klickar på en knapp i formuläret, skickar i begäran den variabel vi behöver - sessionsidentifieraren!
Av uppenbara skäl läggs identifieraren endast till i relativa länkar.

Teoretiskt kan du i våra hemmagjorda sessioner om cookies och databasen manuellt tilldela ID-överföring till alla länkar - och sedan vår egna sessioner kommer att fungera oavsett cookies. Men håller du med - det är trevligare när någon annan gör det här? ;-)

Standard i senaste versionerna PHP har båda alternativen aktiverade. Hur hanterar PHP detta? Cook ställs alltid ut. Och länkar autoslutförs endast om PHP inte upptäckte en cookie med en sessionsidentifierare. När en användare besöker webbplatsen för första gången under denna session, placeras en cookie och länkarna fylls i. Vid nästa begäran, om cookies stöds, ser PHP cookien och slutar fylla i länkar. Om cookies inte fungerar fortsätter PHP att lägga till id till länkar och sessionen går inte förlorad.
Användare med cookies aktiverade kommer bara att se den långa länken med ID en gång.

Puh. ID-överföringen är klar.
Nu återstår bara att binda datafilen till den på serversidan.
PHP kommer att göra detta åt oss. Det räcker med att skriva
session_start();
$_SESSION [ "test" ]= "Hej världen!" ;

Och PHP kommer att skriva testvariabeln till filen som är associerad med denna session.
Det finns en mycket viktig anmärkning här.
Array $_SESSION- speciellt.
Faktum är att den innehåller de variabler som vi vill göra tillgängliga i olika skript.
För att placera en variabel i en session, tilldela den bara till arrayelementet $_SESSION.
För att få dess värde, gå bara till samma element. Ett exempel kommer nedan.

PHP hanterar även sophämtning - tar bort föråldrade filer. Samt datakodning och en massa andra nödvändiga saker. Som ett resultat av denna omsorg är arbetet med sessioner mycket enkelt.
Här kommer vi faktiskt till exemplet på hur sessioner fungerar.
Ett mycket litet exempel:
session_start();

eko "Du har uppdaterat den här sidan". $_SESSION["räknare"]++. " en gång. " ;
eka"
uppdatering" ;
?>

Vi kontrollerar om vi har en räknarvariabel i sessionen om inte, då skapar vi den med värdet 0, och visar sedan dess värde och ökar det med ett. Det ökade värdet kommer att skrivas till sessionen, och nästa gång skriptet anropas kommer variabeln att ha värdet 1, och så vidare.
Allt är väldigt enkelt.

För att få tillgång till sessionsvariabler på alla sidor på webbplatsen behöver du ENDAST skriva EN (!) rad i början av VARJE fil där vi behöver sessioner:
session_start();
Och få tillgång till elementen i $_SESSION-arrayen. Till exempel skulle en auktoriseringskontroll se ut ungefär så här:
session_start();
if ($_SESSION [ "auktoriserad" ]<> 1 ) {
header("Plats: /auth.php" );
utgång;
}

Ta bort variabler från en session.
Om du har register_globals=off så är det bara att skriva
unset($_SESSION [ "var" ]);
Om inte, då nära Jag måste skriva med henne
session_unregister("var");

De vanligaste felen som PHP producerar när man försöker arbeta med sessioner är följande:
Två av dem
Varning: Kan inte skicka sessionscookie - rubriker har redan skickats
Varning: Kan inte skicka sessionscachebegränsare - rubriker har redan skickats

orsakad av samma anledning beskrivs lösningen i denna tråd
Tredje,
Varning: open(/tmp\sess_SID, O_RDWR) misslyckades: Ingen sådan fil eller katalog (2) i full_script_path på radnummer(tidigare såg hon ut som Varning: Det gick inte att skriva sessionsdata (filer). Kontrollera att den aktuella inställningen för session.save_path är korrekt (/tmp)),
om det översätts från engelska, förklarar det problemet i detalj: sökvägen till katalogen som anges i php.ini där sessionsfilerna skrivs är inte tillgänglig. Det här felet är det enklaste att åtgärda. Registrera bara en katalog som finns och är skrivbar, t.ex.
session.save_path = c:\windows\temp
Och glöm inte att starta om Apache efter detta.

Som det visar sig har mänsklig intelligens inga gränser, och därför är jag tvungen att förklara:
ett meddelande om det tredje felet (katalogen kan inte hittas) kommer OUNDVIKLIGT att leda till uppkomsten av de två första, eftersom felmeddelandet matas ut till webbläsaren och rubrikerna efter att det inte kan användas. Skynda dig därför inte att leta efter en för tidig slutsats, utan skriv först ner den rätta vägen!

Det näst vanligaste problemet när man arbetar med sessioner är det tunga arvet från register_globals. Ge INTE skriptvariabler namn som matchar indexen för $_SESSION-matrisen!
Med register_globals=på kommer värdena att skriva över varandra och du kommer att bli förvirrad.
Och om register_globals=av, kommer ett annat fel att visas: "Ditt skript är möjligen beroende av en sessionsbieffekt som existerade fram till PHP 4.2.3.", om skriptet har en sessionsvariabel som inte har något värde och en global variabel med samma namn . För att bli av med det måste du alltid initiera variabler före användning (eller åtminstone kontrollera om de finns) och inte ge globala variabler namn som sammanfaller med indexen för arrayen $_SESSION.

Om det inte fungerar, men inga meddelanden visas, lägg sedan till två rader i början av skriptet som ansvarar för att visa ALLA fel på skärmen - det är mycket möjligt att det finns fel, men du ser dem bara inte.
ini_set("display_errors" , 1 );
error_reporting(E_ALL);

eller se fel i error_log. I allmänhet ligger ämnet för att visa felmeddelanden utanför ramen för den här artikeln, så se bara till att du åtminstone kan se dem. Du kan läsa lite mer om att hitta fel i det här avsnittet.

Om du är säker på att det inte finns några fel, men exemplet som ges inte fungerar ändå, så kanske PHP inte möjliggör att skicka id via URL, och cookies av någon anledning fungerar inte.
Titta vad som är fel med dina kakor.
I allmänhet, om dina sessioner inte fungerar, försök först skicka sessionsidentifieraren manuellt, det vill säga skapa en länk och tilldela en identifierare till den:
session_start();
if (!isset($_SESSION [ "räknare" ])) $_SESSION [ "räknare" ]= 0 ;
eko "Du har uppdaterat den här sidan". $_SESSION["räknare"]++. " en gång.

uppdatering" ;
?>

Du bör dock se till att session.use_only_cookies-direktivet inte är aktiverat, vilket hindrar PHP från att acceptera sessions-ID om det skickades via URL

Om detta exempel inte fungerar är problemet antingen trivialt stavfel(hälften av "problemen" med sessioner kommer från ett felstavat variabelnamn), eller också gammal version PHP: sessionsstöd dök upp i version 4.0 och arrayen $_SESSION- i 4.1 (Innan detta användes det $HTTP_SESSION_VARS).
Om det fungerar ligger problemet i cookies. Övervaka vilken typ av cookie servern ställer in i webbläsaren och om webbläsaren returnerar den. Det är mycket användbart att söka genom att titta på utbytet av HTTP-rubriker mellan webbläsaren och servern.
En förklaring av hur cookies fungerar ligger utanför ramen för denna redan för långa text, men se åtminstone till att servern skickar en cookie med en identifierare, och webbläsaren returnerar den. Och samtidigt sammanfaller identifierarna med varandra =)
Att ställa in kakan ska se ut
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6;
eller hur
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; sökväg =/
(om du begär skriptet inte från rotkatalogen)
Serverns svar bör se ut
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6
eller
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b
om webbläsaren returnerar andra cookies än sessions-ID.

Om webbläsaren inte returnerar cookies, kontrollera om cookies överhuvudtaget fungerar.
Se till att domänen du kommer åt har ett normalt namn (med minst en prick och inga olagliga tecken som understreck) och rensa webbläsarens cache - det är två huvudorsaker till varför cookies kanske inte fungerar.

Om exemplet härifrån fungerar, men din egen kod inte gör det, så ligger problemet uppenbarligen inte i sessionerna, utan i algoritmen. Leta efter var du tappade variabeln, överför exemplet härifrån steg för steg och felsök ditt skript.

Ett annat problem kan uppstå om du använder rubrikomdirigering eller JavaScript-navigering.
Faktum är att PHP automatiskt lägger till sessionsidentifieraren endast till länkar som , men gör inte detta för rubriker, JavaScript, metataggar.
Därför måste du lägga till identifieraren manuellt, till exempel så här:
header("Plats: /script.php?" . sessionsnamn(). "=" . session_id());

Ett mycket sällsynt problem, och det är helt oklart var det kommer ifrån, är att session.save_handler-inställningen har ett värde som skiljer sig från filer. Om det inte är så, korrigera det.

Säkerhet
Sessionssäkerhet är ett brett ämne. Därför kommer jag att fokusera på några huvudpunkter.
Det mest lärobokade är att inte skicka identifieraren genom adressfältet. Detta är skrivet även i php.ini, men detta begränsar sessionernas funktionalitet. Om du bestämmer dig för att följa detta råd, så glöm inte session.use_only_cookies = 1, förutom session.use_trans_sid = 0
Det är tillrådligt att binda sessionen till en IP-adress: på detta sätt, om identifieraren blir stulen, kommer skurken fortfarande inte att kunna använda den i de flesta fall.
Det rekommenderas att använda session.save_path-direktivet, som låter dig ställa in din egen katalog för att spara sessionsfiler. Detta är säkrare än att ha dem lagrade i serverns delade temporära standardkatalog.

Ytterligare information:

  • Förutom cookies skickar sessionsmekanismen även rubriker som förbjuder sidcache (samma cachebegränsare). För html är detta korrekt och nödvändigt. Men när du försöker skicka en fil med ett skript som kontrollerar auktorisering, vägrar Internet Explorer att ladda ner den. Det är på grund av den här titeln. Ring upp
    session_cache_limiter("privat");
    måste lösa problemet innan sessionen påbörjas.
  • Hur konstigt det än kan tyckas, men i uppsättningen $_SESSION Du kan inte använda numeriska index - $_SESSION [ 1 ], $_SESSION [ "10" ]- sessioner kommer inte att fungera.
  • Någonstans mellan version 4.2 och 5.0 var det inte möjligt att ställa in session.use_trans_sid med ini_set(). Från och med 5.0 är det redan möjligt igen.
  • Före version 4.3.3 av cookien skickade PHP en cookie endast om det inte fanns någon identifierare i begäran när sessionen startade. Nu skickas en cookie vid varje samtal session_start()

    Exempel på auktorisering med användning av sessioner
    Låt oss illustrera allt ovan med ett litet exempel:
    Låt oss skapa filen auth.php:
    if (isset($_POST [ "auth_name" ]))
    {
    $sql = "SELECT * FROM users WHERE name=?s";
    $row = $db -> getRow($sql, $_POST["auth_name"]);
    if ($row && password_verify ($_POST [ "auth_pass" ], $row [ "pass" ])) (
    $_SESSION [ "user_id" ] = $rad [ "id" ];
    }
    header("Plats: http://" . $_SERVER [ "HTTP_HOST" ]. $_SERVER [ "REQUEST_URI" ]);
    utgång;
    }

    if (isset($_GET [ "action" ]) OCH $_GET [ "action" ]== "logga ut" ) (
    session_start();
    session_destroy();
    header("Plats: http://" . $_SERVER [ "HTTP_HOST" ]. "/" );
    utgång;
    }

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








    utgång;
    }

    Nu behöver du bara skriva raden i alla skyddade skript
    kräver "auth.php" ;
    Det här exemplet förutsätter att sessionen redan har startat och en anslutning till databasen har skapats med klassen för säkert och bekvämt arbete med MySQL. Det förutsätter också att lösenordet hashas med den rekommenderade password_hash-funktionen.
    Exempel på en skyddad fil:

    session_start();
    inkludera "safemysql.class.php" ;
    $db = new safemysql ([ "db" => "test" ]);
    inkludera "auth.php" ;
    ?>
    hemlighet

    logga ut

    OPS! Mycket användbara länkar:
    http://www.php.net/manual/ru/ref.session.php - den senaste och senaste informationen om sessionsstöd i PHP i den officiella dokumentationen, plus många användarkommentarer. Rekommenderad läsning.
    http://phpclub.ru/manrus/f/ref.session.html - En MYCKET föråldrad översättning av detta kapitel till ryska, från dokumentationen översatt av Alexander Pyramidin.
    http://phpclub.ru/detail/article/sessions
    En artikel med den patetiska titeln "The Truth about Sessions." Lämnar ett ambivalent intryck. I början talar författaren VÄLDIGT tydligt om sessionsmekanismen, men de metoder han erbjuder i slutet av artikeln är helt oklara.

    En läroboksartikel av Dmitry Borodin från sajten
    http://php.spb.ru/ rekommenderas INTE.
    Killar, det är fruktansvärt föråldrat. Det innehåller inte bara faktafel, utan sessioner i PHP har helt enkelt inte arbetats med på länge.
    Stort tack till Dima för det, det här var den första artikeln om sessionerna på ryska, jag studerade själv från den, men nu måste jag skicka den till en välförtjänt vila.
    Tyvärr är många andra artiklar på Internet som inte har uppdaterats på flera år också föråldrade.

  • Hälsningar, kära gemenskap.

    Först och främst vill jag tacka er för mycket användbar resurs. Mer än en gång har jag hittat många intressanta idéer och praktiska råd här.

    Syftet med den här artikeln är att belysa fallgroparna med att använda sessioner i PHP. Naturligtvis finns det dokumentation om PHP och en hel del exempel, och den här artikeln låtsas inte vara det komplett guide. Den är utformad för att avslöja några av nyanserna i att arbeta med sessioner och skydda utvecklare från onödigt slöseri med tid.

    Det vanligaste exemplet på användning av sessioner är naturligtvis användarauktorisering. Låt oss börja med den mest grundläggande implementeringen för att gradvis utveckla den allt eftersom nya uppgifter dyker upp.

    (För att spara utrymme och tid kommer vi att begränsa våra exempel till enbart själva sessionsfunktionerna, istället för att här bygga en fullfjädrad testapplikation med en vacker klasshierarki, omfattande felhantering och annat bra).

    Funktion startSession() ( // Om sessionen redan har startat, sluta köra och returnera TRUE // (parametern session.auto_start i php.ini-inställningsfilen måste vara inaktiverad - standardvärdet) if (session_id()) returnerar true; annars returnerar session_start(); // Obs: Före version 5.3.0 returnerade session_start()-funktionen TRUE även om det fanns ett fel // Om du använder en tidigare version än 5.3.0, gör en extra kontroll för session_id() // efter att ha anropat session_start()-funktionen destroySession() ( if (session_id()) ( // Om det finns en aktiv session, radera sessionscookies, setcookie(session_name(), session_id(), time(. )-60*60*24); // och förstör sessionen session_unset();

    Notera: Det antyds att grundläggande kunskap Läsaren känner till PHP-sessioner, så vi kommer inte att täcka funktionsprincipen för funktionerna session_start() och session_destroy() här. Uppgifterna med layout av inloggningsformuläret och användarautentisering är inte relaterade till ämnet för artikeln, så vi kommer också att utelämna dem. Låt mig bara påminna dig om att för att identifiera användaren i varje efterföljande begäran, vid tidpunkten för lyckad inloggning, måste vi lagra användaridentifieraren i en sessionsvariabel (som heter användarid, till exempel), som kommer att vara tillgänglig i alla efterföljande förfrågningar inom sessionens liv. Det är också nödvändigt att implementera bearbetning av resultatet av vår startSession() funktion. Om funktionen returnerar FALSE, visa inloggningsformuläret i webbläsaren. Om funktionen returnerade TRUE, och en sessionsvariabel som innehåller identifieraren för den auktoriserade användaren (i vårt fall - användar-id), finns - visa sidan för den auktoriserade användaren (för mer information om felhantering, se tillägget daterat 2013-06- 07 i avsnittet om sessionsvariabler).

    Än så länge är allt klart. Frågor börjar när du behöver implementera användarinaktivitetskontroll (session timeout), göra det möjligt för flera användare att arbeta samtidigt i en webbläsare och även skydda sessioner från obehörig användning. Detta kommer att diskuteras nedan.

    Övervaka användarinaktivitet med inbyggda PHP-verktyg

    Den första frågan som ofta dyker upp bland utvecklare av alla typer av konsoler för användare är automatisk avslutning av sessionen i händelse av inaktivitet från användarens sida. Det finns inget enklare än att göra detta med de inbyggda funktionerna i PHP. (Detta alternativ är inte särskilt tillförlitligt eller flexibelt, men vi kommer att överväga det för fullständighetens skull).

    Funktion startSession() ( // Timeout för användarinaktivitet (i sekunder) $sessionLifetime = 300; if (session_id()) returnerar true; // Ställ in cookie-livslängden ini_set("session.cookie_lifetime", $sessionLifetime); // Om användare timeout för inaktivitet är inställd, ställ in sessionens livslängd på servern // Obs: För en produktionsserver rekommenderas det att förinställa dessa parametrar i php.ini-filen if ($sessionLifetime) ini_set("session.gc_maxlifetime", $sessionLifetime if (session_start(); )) (setcookie(session_name(), session_id(), time()+$sessionLifetime); return true; ) else return false)

    Några förtydliganden. Som du vet bestämmer PHP vilken session som måste startas av det cookienamn som skickas av webbläsaren i begäranshuvudet. Webbläsaren i sin tur tar emot denna cookie från servern, där session_start()-funktionen placerar den. Om webbläsarcookien har gått ut kommer den inte att skickas i begäran, vilket innebär att PHP inte kommer att kunna avgöra vilken session som ska starta och kommer att behandla detta som att skapa en ny session. PHP-inställningsparametern session.gc_maxlifetime, som är inställd på vår timeout för användarinaktivitet, ställer in livstiden för en PHP-session och kontrolleras av servern. Att kontrollera sessionens livslängd fungerar enligt följande (här ser vi ett exempel på att lagra sessioner i temporära filer som det vanligaste och standardalternativet i PHP).

    När en ny session skapas skapas en fil som heter sess_ i katalogen som anges som sessionslagringskatalog i PHP-inställningsparametern session.save_path , Var - sessionsidentifierare. Därefter, i varje begäran, vid tidpunkten för start av en redan befintlig session, uppdaterar PHP ändringstiden för denna fil. Alltså i varje nästa PHP-förfrågan, genom skillnaden mellan den aktuella tiden och den senaste ändringstiden för sessionsfilen, kan avgöra om sessionen är aktiv eller om dess livslängd redan har löpt ut. (Mekanismen för att ta bort gamla sessionsfiler diskuteras mer i detalj i nästa avsnitt.)

    Notera: Det bör noteras här att parametern session.gc_maxlifetime gäller för alla sessioner inom en server (mer exakt inom en PHP-huvudprocess). I praktiken betyder detta att om flera webbplatser körs på servern, och var och en av dem har sin egen timeout för användarinaktivitet, kommer inställning av denna parameter på en av webbplatserna att leda till att den ställs in för andra webbplatser. Detsamma gäller delad hosting. För att undvika denna situation används separata sessionskataloger för varje plats inom samma server. Att ställa in sökvägen till sessionskatalogen görs med parametern session.save_path i inställningsfilen php.ini, eller genom att anropa ini_set()-funktionen. Efter detta kommer sessionerna för varje webbplats att lagras i separata kataloger, och parametern session.gc_maxlifetime som ställts in på en av webbplatserna kommer endast att vara giltig för dess session. Vi kommer inte att överväga det här fallet i detalj, särskilt eftersom vi har ett mer flexibelt alternativ för att övervaka användarinaktivitet.

    Styra användarinaktivitet med hjälp av sessionsvariabler

    Det verkar som att det tidigare alternativet, för all sin enkelhet (bara ett par extra rader kod), ger allt vi behöver. Men vad händer om inte varje begäran kan ses som ett resultat av användaraktivitet? Till exempel har en sida en timer som med jämna mellanrum gör en AJAX-begäran för att ta emot uppdateringar från servern. En sådan begäran kan inte betraktas som användaraktivitet, vilket innebär att en automatisk förlängning av sessionens livslängd inte är korrekt i detta fall. Men vi vet att PHP uppdaterar ändringstiden för sessionsfilen automatiskt varje gång session_start()-funktionen anropas, vilket innebär att varje begäran kommer att leda till en förlängning av sessionens livslängd, och tidsgränsen för användarinaktivitet kommer aldrig att inträffa. Dessutom kan den sista anteckningen från föregående avsnitt om krångligheterna med parametern session.gc_maxlifetime verka för förvirrande och svår att implementera för vissa.

    För att lösa detta problem kommer vi att överge användningen av inbyggda PHP-mekanismer och introducera flera nya sessionsvariabler som gör att vi själva kan kontrollera tiden för användarinaktivitet.

    Funktion startSession($isUserActivity=true) ($sessionLifetime = 300; if (session_id()) returnerar true; // Ställ in cookie-livslängden innan du stänger webbläsaren (vi kommer att kontrollera allt på serversidan) ini_set("session. cookie_lifetime", 0); if (! session_start()) returnerar false; $t = time(); if ($sessionLifetime) ( // Om användarens inaktivitetstid är inställd, // kontrollera tiden som har gått sedan användarens senaste aktivitet // (tidpunkten för den senaste begäran) när lastactivity-sessionsvariabeln uppdaterades) if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) ( // Om tid som förflutit sedan användarens senaste aktivitet, // är större än timeouten för inaktivitet, vilket betyder att sessionen har löpt ut och sessionen måste avslutas destroySession(); begäran kom som ett resultat av användaraktivitet, // uppdatera lastactivity-variabeln med värdet för den aktuella tiden, // förlänger därmed sessionstiden med ytterligare en sessionLifetime-sekunder om ($isUserActivity) $_SESSION["lastactivity"] = $t; ) ) return true; )

    Låt oss sammanfatta. I varje begäran kontrollerar vi om timeouten har uppnåtts sedan den senaste användaraktiviteten fram till det aktuella ögonblicket, och om den har uppnåtts förstör vi sessionen och avbryter exekveringen av funktionen, vilket returnerar FALSE. Om timeout inte har uppnåtts och $isUserActivity-parametern med värdet TRUE skickas till funktionen uppdaterar vi tiden för användarens senaste aktivitet. Allt vi behöver göra är att avgöra i anropsskriptet om begäran är resultatet av användaraktivitet, och om inte, anropa startSession-funktionen med $isUserActivity-parametern inställd på FALSE.

    Uppdatering från 2013-06-07
    Bearbetar resultatet av sessionStart()-funktionen

    Kommentarerna påpekade att returnering av FALSE inte ger en fullständig förståelse av orsaken till felet, och detta är helt rättvist. Jag har inte publicerat detaljerad felhantering här (artikelns längd är redan ganska stor), eftersom detta inte är direkt relaterat till artikelns ämne. Men med tanke på kommentarerna ska jag förtydliga.

    Som du kan se kan sessionStart-funktionen returnera FALSE i två fall. Antingen kunde sessionen inte startas på grund av vissa interna serverfel (till exempel felaktiga sessionsinställningar i php.ini), eller så har sessionens livslängd gått ut. I det första fallet måste vi omdirigera användaren till en sida med ett felmeddelande om att det finns problem på servern och ett formulär för att kontakta support. I det andra fallet måste vi överföra användaren till inloggningsformuläret och visa ett motsvarande meddelande i det om att sessionen har löpt ut. För att göra detta måste vi ange felkoder och returnera motsvarande kod istället för FALSE, och kontrollera den i anropsmetoden och agera därefter.

    Nu, även om en session på servern fortfarande existerar, kommer den att förstöras första gången den öppnas om användarens tidsgräns för inaktivitet har löpt ut. Och detta kommer att hända oavsett vilken sessionslivslängd som är inställd i de globala PHP-inställningarna.

    Notera: Vad händer om webbläsaren stängdes och sessionsnamncookien förstördes automatiskt? Begäran till servern nästa gång webbläsaren öppnas kommer inte att innehålla sessionscookies, och servern kommer inte att kunna öppna sessionen och kontrollera användarens inaktivitetstidsgräns. För oss motsvarar detta att skapa en ny session och påverkar inte funktionalitet eller säkerhet på något sätt. Men en rättvis fråga uppstår - vem kommer då att förstöra den gamla sessionen, om vi fram till nu har förstört den efter att timeouten har gått ut? Eller kommer det nu att hänga i sessionskatalogen för alltid? För att rensa upp gamla sessioner i PHP finns det en mekanism som kallas skräpinsamling. Den körs vid tidpunkten för nästa begäran till servern och rensar alla gamla sessioner baserat på det senaste ändringsdatumet för sessionsfilerna. Men sopsamlingsmekanismen startar inte med varje begäran till servern. Frekvensen (eller snarare sannolikheten) för lansering bestäms av två inställningsparametrar session.gc_probability och session.gc_divisor. Resultatet av att dividera den första parametern med den andra är sannolikheten för att starta sophämtningsmekanismen. För att sessionsrensningsmekanismen ska kunna startas med varje begäran till servern måste dessa parametrar ställas in på lika värden, till exempel "1". Detta tillvägagångssätt garanterar en ren sessionskatalog, men är uppenbarligen för dyrt för servern. På produktionssystem är därför standardvärdet för session.gc_divisor satt till 1000, vilket betyder att sopsamlingsmekanismen kommer att köras med en sannolikhet på 1/1000. Om du experimenterar med dessa inställningar i din php.ini-fil, kanske du märker att i det fall som beskrivs ovan, när webbläsaren stänger och rensar alla sina cookies, finns det fortfarande gamla sessioner kvar i sessionskatalogen ett tag. Men det här borde inte oroa dig, för... som redan nämnts påverkar detta inte på något sätt säkerheten för vår mekanism.

    Uppdatering från 2013-06-07

    Förhindrar att skript fryser på grund av låsning av sessionsfiler

    Kommentarerna tog upp frågan om att samtidigt köra skript som fryser på grund av att sessionsfilen blockeras (det mest slående alternativet är lång omröstning).

    Till att börja med noterar jag att detta problem inte direkt beror på serverbelastningen eller antalet användare. Naturligtvis än fler förfrågningar, desto långsammare körs skripten. Men detta är ett indirekt beroende. Problemet uppträder bara inom en session, när servern tar emot flera förfrågningar på uppdrag av en användare (till exempel en av dem är lång omröstning, och resten är vanliga förfrågningar). Varje begäran försöker komma åt samma sessionsfil, och om den tidigare begäran inte låste upp filen, kommer den efterföljande att hänga och vänta.

    För att hålla sessionsfillåsningen till ett minimum rekommenderas det starkt att stänga sessionen genom att anropa session_write_close()-funktionen omedelbart efter att alla åtgärder med sessionsvariabler har slutförts. I praktiken betyder det att du inte ska lagra allt i sessionsvariabler och komma åt dem under hela körningen av skriptet. Och om du behöver lagra en del arbetsdata i sessionsvariabler, läs dem omedelbart när sessionen startar, spara dem i lokala variabler för senare användning och stäng sessionen (vilket innebär att stänga sessionen med session_write_close-funktionen och inte förstöra den med session_destroy ).

    I vårt exempel betyder detta att vi omedelbart efter att ha öppnat en session, kontrollerat dess livslängd och att det finns en auktoriserad användare måste läsa och spara alla ytterligare krävs av ansökan sessionsvariabler (om några finns), stäng sedan sessionen med ett anrop till session_write_close() och fortsätt att köra skriptet, vare sig det är en lång omröstning eller en vanlig begäran.

    Skyddar sessioner från obehörig användning

    Låt oss föreställa oss situationen. En av dina användare får en trojan som rånar webbläsarcookies (där vår session lagras) och skickar den till angiven e-post. Angriparen skaffar cookien och använder den för att förfalska en begäran på uppdrag av vår auktoriserade användare. Servern accepterar och behandlar denna begäran som om den kom från en auktoriserad användare. Om ytterligare verifiering av IP-adressen inte implementeras kommer en sådan attack att leda till ett framgångsrikt hackning av användarens konto med alla efterföljande konsekvenser.

    Varför var detta möjligt? Uppenbarligen, eftersom namnet och sessionsidentifieraren alltid är desamma under hela sessionens livslängd, och om du får dessa data, kan du enkelt skicka förfrågningar på uppdrag av en annan användare (naturligtvis inom sessionens livstid). Detta kanske inte är den vanligaste typen av attack, men teoretiskt sett verkar det ganska genomförbart, särskilt med tanke på att en sådan trojan inte ens behöver administratörsrättigheter för att råna användarens webbläsarcookies.

    Hur kan du skydda dig mot attacker av det här slaget? Återigen, uppenbarligen, genom att begränsa livslängden för sessionsidentifieraren och periodiskt ändra identifieraren inom samma session. Vi kan också ändra namnet på sessionen genom att helt ta bort den gamla och skapa en ny session, kopiera alla sessionsvariabler från den gamla in i den. Men detta påverkar inte kärnan i tillvägagångssättet, så för enkelhetens skull kommer vi att begränsa oss till endast sessionsidentifieraren.

    Det är tydligt att ju kortare sessions-ID:s livslängd, desto mindre tid kommer en angripare att behöva skaffa och använda cookies för att förfalska en användarförfrågan. Helst bör en ny identifierare användas för varje begäran, vilket kommer att minimera möjligheten att använda någon annans session. Men vi kommer att överväga det allmänna fallet när sessionsidentifierarens regenereringstid ställs in godtyckligt.

    (Vi kommer att utelämna den del av koden som redan har diskuterats).

    Funktion startSession($isUserActivity=true) (// Sessionsidentifierares livstid $idLifetime = 60; ... if ($idLifetime) ( // Om sessionsidentifierarens livslängd är inställd, // kontrollera tiden som förflutit sedan sessionen var skapad eller den senaste regenereringen // (tidpunkten för den senaste begäran när sessionsvariabeln starttid uppdaterades) if (isset($_SESSION["starttid"])) (if ($t-$_SESSION["starttid"] >= $ idLifetime) ( // Tid för sessionsidentifierarens livslängd // Generera en ny identifierare session_regenerate_id(true); $_SESSION["starttid"] = $t) ) else ( // Vi kommer hit om sessionen precis har skapats // Ställ in genereringstiden för sessionsidentifieraren till aktuell tid$_SESSION["starttid"] = $t; ) ) return true; )

    Så när vi skapar en ny session (som inträffar när användaren loggar in), ställer vi in ​​sessionsvariabeln starttid, som lagrar tiden för den senaste generationen av sessionsidentifieraren, till ett värde lika med den aktuella servertiden. Därefter kontrollerar vi i varje begäran om det har gått tillräckligt med tid (idLifetime) sedan den senaste generationen av identifieraren, och i så fall genererar vi en ny. Således, om angriparen som tog emot cookien från den auktoriserade användaren under identifierarens inställda livslängd inte har tid att använda den, kommer den falska begäran att betraktas av servern som obehörig, och angriparen kommer att tas till inloggningssidan .

    Notera: Det nya sessions-ID:t hamnar i webbläsarens cookie när session_regenerate_id()-funktionen anropas, vilket skickar den nya cookien, liknande session_start()-funktionen, så vi behöver inte uppdatera cookien själva.

    Om vi ​​vill göra våra sessioner så säkra som möjligt räcker det att ställa in livslängden för identifieraren till en eller till och med ta bort session_regenerate_id()-funktionen från parentes och ta bort alla kontroller, vilket kommer att leda till att identifieraren återskapas i varje begäran. (Jag har inte testat effekten av detta tillvägagångssätt på prestanda, och jag kan bara säga att session_regenerate_id(true)-funktionen i huvudsak endast utför fyra åtgärder: generera en ny identifierare, skapa en rubrik med sessionscookien, ta bort den gamla och skapa en ny sessionsfil).

    Lyrisk utvikning: Om trojanen visar sig vara så smart att den inte kommer att skicka cookies till angriparen, utan organiserar sändningen av en förberedd falsk begäran omedelbart efter mottagandet av kakan, kommer metoden som beskrivs ovan med största sannolikhet inte att kunna skydda mot sådana en attack, eftersom det är praktiskt taget ingen skillnad mellan den tid då trojanen tar emot kakan och sändningen av den falska begäran, och det finns en stor sannolikhet att sessionsidentifieraren för närvarande inte kommer att återskapas.

    Möjlighet till samtidigt arbete i en webbläsare för flera användares räkning

    Den sista uppgiften som jag skulle vilja överväga är möjligheten för flera användare att arbeta samtidigt i en webbläsare. Den här funktionen är särskilt användbar i teststadiet, när du behöver emulera användarnas samtidiga arbete, och det är tillrådligt att göra detta i din favoritwebbläsare, snarare än att använda hela den tillgängliga arsenalen eller öppna flera instanser av webbläsaren i inkognitoläge .

    I våra tidigare exempel angav vi inte explicit ett sessionsnamn, så standard PHP-namnet (PHPSESSID) användes. Det betyder att alla sessioner vi har skapat hittills har skickat en cookie till webbläsaren under namnet PHPSESSID. Uppenbarligen, om cookienamnet alltid är detsamma, finns det inget sätt att organisera två sessioner med samma namn i samma webbläsare. Men om vi använde vårt eget sessionsnamn för varje användare skulle problemet vara löst. Låt oss göra det.

    Funktion startSession($isUserActivity=true, $prefix=null) ( ... if (session_id()) returnerar true; // Om användarprefixet skickas i parametrarna, // ställ in ett unikt sessionsnamn som inkluderar detta prefix, // annars ställ in vanligt namn för alla användare (till exempel MYPROJECT) session_name("MYPROJECT".($prefix ? "_".$prefix: "")); om (! session_start()) returnerar false;

    Nu återstår bara att se till att det anropande skriptet skickar ett unikt prefix för varje användare till startSession()-funktionen. Detta kan till exempel göras genom att skicka ett prefix i GET/POST-parametrarna för varje begäran eller genom en extra cookie.

    Slutsats

    Avslutningsvis kommer jag att tillhandahålla den fullständiga slutliga koden för våra funktioner för att arbeta med PHP-sessioner, inklusive alla uppgifter som diskuterats ovan.

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

    Jag hoppas att den här artikeln kommer att spara lite tid för dem som aldrig har fördjupat sig för djupt i sessionsmekanismen, och ge tillräckligt med insikt i denna mekanism för dem som precis har börjat bekanta sig med PHP.