Sessions v PHP funguje defaultně tak, že se pro každý nový požadavek vygeneruje unikátní ID, které se pak posílá tam a zpět přes cookies. Na disku na serveru se pak obvykle v /tmp adresáři vytvoří soubor sess_$ID (takže třeba například /tmp/sess_vc816c8hj4o3ukpicaisa340d4) do kterého se ukládají data pro dané sezení. Při dalším přístupu toho stejného uživatele už se podle jeho session_id (z cookies) na disku načte ten správný soubor a z něj se data vytáhnou. Čas od času se pak tyhle dočasné soubory zase mažou.
Obecně je to univerzální a jednoduchý způsob, ovšem kdokoli může takovéto soubory číst (ať už uživatel pod kterým běží web, nebo i administrátor systému). Nemusím jistě také nikomu vysvětlovat, že přímý přístup na filesystém je jedna z nejhorších možností, kterou použít, zvlášť pokud má systém evidovat desetitisíce sessions najednou.
Je tedy vhodné si session handling napsat extra sám. Vlastní implementace sessions, kterou teď zvažuju použít já, vychází z následujících požadavků:
- neukládat data do jednotlivých souborů, ale do databáze
- vyřešit, aby nemohl kdokoli číst data ostatních, ani systém administrátor
- přimět PHP, aby se takto o sessions staralo samo, bez změn ostatního kódu
Řešení:
PHP umožňuje programátorovi nadefinovat vlastní funkce pro čtení a zápis sezení, a to pomocí funkce session_set_save_handler(), takže stačí jen napsat pár vlastních funkcí a PHPčku říct, že je má používat. Tyto funkce pak data ukládají tam kam chci, tedy do databáze.
Uložené data je v databázi potřeba nějak šifrovat, ovšem šifrovací klíč není možné do databáze uložit, jinak by to celé postrádalo smysl. Je nasnadě šifrovat data pomocí session identifikátoru, který se posílá s každým požadavkem. Nemůžu pak ale tento identifikátor v databázi uložit, takže je pak nutné pro identifikaci správných dat pro session použít něco jiné – třeba hash toho session_id (s potencionálním rizikem kolize, které je sice titěrné ale nenulové), nebo zakódovanou verzi nějakého pevného stringu tím session_id (důležitá otázka, nemůže i toto vést ke kolizi?)
V databázi vytvořím tabulku sessions, se třemi sloupci:
Implementaci funkcí v PHP uložím do souboru třeba lib_session.php. Tenhle blog na rootu neumí syntax highlighting ani formátování kódu, takže jsem výsledek pastnul na pastebin. Pro zjednodušení předpokládám existenci funkcí query(), escape(), fetch_assoc(), jejichž význam je evidentní. Stačí pak tento soubor includnout do všech PHP skriptů, kde chci se session pracovat.
Vypíchnu SQL příkazy, které vlastní práci s daty provádí. Tohle zapisuje data session do databáze:
REPLACE sessions SET
id=AES_ENCRYPT("nejaky string furt stejny", $session_id),
lastUpdate=NOW(),
sessionData=AES_ENCRYPT($data, $session_id)
--
A tohle čte session data z databáze:
SELECT AES_DECRYPT(sessionData, $session_id)
FROM sessions
WHERE id=AES_ENCRYPT("nejaky string furt stejny", $session_id)
--
Přijde mi to jako neprůstřelné řešení. Není? Budu rád za Vaše komentáře.
[2]: Obavam sa, ze tymto tvrdenim si prave vyrazne znizil svoju kredibilitu. Nie preto, ze neprogramujes objektovo, ale preto, ze ZASADNE nepouzivas nastroje... To je podla mna pri programovani najhorsie - programator, ktory uz len z presvedcenia nechce nieco pouzivat. Ak uz nieco nepouzivam, tak kvoli tomu, ze sa mi to momentalne na danom projekte nehodi a nie preto, lebo zo zasady nie. :)
[6] Viděl jsem to na několika dost zatížených serverech (s desítkama tisíc přihlášených uživatelů) a oproti databázi to výrazně pomohlo. Jeden čas na tom běžel i Facebook. A i když je to už fousaté řešení, pro rychloobrátková data je funkční a jednoduché a laciné. Není tam moc co by se mohlo rozbít.
[8] např. můžete mít víc instancí memcached. Nicméně typicky se používá na VPSkách nebo dedikovaných serverech. Tam, kde je větší zátěž - a kde se data, která teoreticky můžete zahodit nesmí zapsat na disk - běžné databáze se snaží o persistenci, což bere dost výkonu. Jednoduchý přístup do hašové tabulky, kde není žádná snaha o uložení na disk je pořád o řád rychlejší než jaká koliv databáze.
Re [5]: asi jsem se vyjádřil příliš ukvapeně :) Prakticky jsem se nesetkal s úlohou, kde by mi objekty v PHP přinesly takovou hodnotu, aby mi stálo zato je použít. Jsou lidi, co ze zásady píšou všechno objektově, tak já ze zásady se snažím všechno řešit bez objektů, částečně taky proto, že si nepamatuju syntax :) a pak taky asi proto, že je pro můj mozek přirozenější vymýšlet řešení funkcionální. I když musím uznat, že zrovna tenhle příklad s sessionhandlerinterface zrovna vypadá jako ideální kandidát na použití objektově :)
No už delší čas to dělám tak, že session nikam neukládám. Session je řetězec, který si na sobě nese všechny důležité informace, jako userid, platnost, efektivni uroven opravneni a dalsi informace zabalene v bináru, podepsané přes nějaký salt a HMAC-SHA256 a to celé Base64. Pokud bych potřeboval skrýt údaje v session, pak ji mohu ještě AESem zašifrovat (a protože každá session má jiný rozsah platnosti, bude každý řetězec jiný rozsypaný čaj.
Samotná session má platnost nějakou krátkou dobu, třeba 15min, nebo hodinu a rozpoznají ji všechni komponenty, kde se používá, aniž by si musely ta data předávat. Klient (nebo prostřednictvím serveru) je třeba session občas prodloužit, aby nevypršela (změní se ten řetězec).
Nevýhodou těchto session je, že se nedají zneplatnit před vypršením, proto se platnost udržuje krátká.
Pro dlouhodobé session se používá refresh token, který se dá použít k vytvoření session. Tyto token lze udržovat v databázi, není k ním potřeba tak často přistupovat (jen když vyprší původní session).
Celý to je ve shodě se systémem OAuth 2.0
[6] - naopak. na jednom miniwebiku neni potreba se trapit s vykonem a necham to v sql databazi at si s tim poradi. To prave na vetsich webech prichazi veci jako je kombinace storage. Na jednom "vypecenem" projektu jsem kombinoval APC user cache, memcached, mongodb a k tomu jeste SQL. Proc?
- protoze APC je z nich suverene nejrychlejsi(cca 10x oproti memcached). Ma vsak nevyhodu v tom, ze CLI se tam nedostane...
- memcached muzu mit jeden cache server pro vice aplikacnich serveru a ma nulove I/O a CPU zatizeni (takze tam pak muze bezet treba Solr) - sem krome jinyho davame prave sessions.
- mongodb - je persistentni, ale je v urcitych vecech vyrazne rychlejsi nez SQL
- SQL - je persistentni a v urcitych vecech ma vyhody oproti nosql
Já používám v Apachi mod_ruid2 na kompletní oddělení jednotlivých vhostů, takže se mi fakt nestane, že by si uživatelé mohli lézt do zelí a to včetně sessions. Každý klient/vhost má vlastního uživatele a pod ním se všechno čte, zapisuje a spouští.
Takhle vypadají soubory v /var/lib/php/session:
-rw------- 1 karel karel 56 Jun 11 07:16 sess_6gg4mtei5h69v986neg2vt3n16
Root je samozřejmě číst může, ale snažit se tomu zabránit mi přijde jako zbytečná práce.
OMG, dalsi "PHP programator", co dela v PHP3 a mysli si, ze ho to spasi, ze zasady totiz nepouziva objekty, ale programuje v sablonovacim systemu jelikoz 0=='wtf'. Dokonce dojde tak daleko, ze nechce, aby administrator/root mohl cist soubory, to jako fakt myslis vazne? Dalsi prihazujou svoje kapky odbornosti co pochytili na nejakem wordpress foru "Session je řetězec, který si na sobě nese všechny důležité informace, jako userid, platnost, efektivni uroven opravneni a dalsi informace zabalene v bináru, podepsané přes nějaký salt a HMAC-SHA256 a to celé Base64.". Fakt me to rozesmalo. Kluci bezte se pro zacatek aspon naucit trochu to o cem pisete a prestante sem tahat nejaky FB moresy na nejaky blozinky nebo co to "programujete" omfg. Kolo uz jsme objevili hodnekrat, opravdu to nekdo chce objevovat znovu? Dalsi story jak se "php programator" snazi delat "linux admina". gosh rtfm and http://stfu.se
Re 23: nevím, jak nějaký můj kus kódu souvisí s předchozí poznámkou, na kterou asi reaguješ. Odkaz na kus kódu jsem napsal v tom blog postu ... http://pastebin.com/VBKkCNrr
Já netvrdím, že jsem génius, ani se nikomu nevnucuju. Jen mě dost vadí krytika podaná formou viz [21], machrovat umí každej.
K čemu jsou ta data v databázi šifrována, když se do té databáze posílají SQL dotazy, kde ta data jsou nešifrovaná plus se databázi posílá i klíč? Úplně stačí, když se databáze na chvilku zaseká, a klíč se objeví ve slow logu. Pokud má mít to šifrování nějaký smysl, mělo by se šifrovat v PHP před odesláním.
Re 24.: kod ma zaujimal hlavne preto ze pises ze programujes radsej "functional programing", ale tvoj kod o tom nesveci. Hlavna zasada FP je: In functional programming, you have no data represented by variables.In functional programming, you have no data represented by variables.
K stylu akym je napisany tvoj kod sa radsej ani nevyjadrujem. Takto sa programovalo v php cca 10 rokov dozadu. Sorry ale musim dat zapravdu [21] aj ked da sa to napisat aj slusnejsie :-)
Příspěvek [21] je čistě trolling. Vůbec bych se jím nezabýval. Netuším co myslí těmi FB moresy (session nesoucí přímo informace o uživateli používá FB, Google i Seznam, a vsadím se že i další velké společnosti). Nicméně v daném příspěvku neprokázal nic, jen "blbě tlachá", a přesně jak sám píše, bych mu doporučil toto: http://stfu.se
Re [29]: ze by napr. sqlses_encid function? pripadne sqlses_read ? ja sa nemozem ubranit pocitu ze si mylis proceduralne programovanie s funkcionalnym. Tvoje "funkcie" maju side effect cize ich povazujem skor za procedury.
Pre nazornost toto je typicka funkcia z FP:
function criteria_greater_than($min)
{
return function($item) use ($min) {
return $item > $min;
};
}
ako vidis navratova hodnota funkcie je znova funkcia, tak to funguje v FP, zjednodusene povedane "vsetko je funkcia" :-)
preto si myslim ze tvoj kod nie je funkcionalne programovanie
jo asi sme sa nerozumeli :-) tak ci onak ak ta to zaujima tak napr tu tak velmi strucne: http://www.phptherightway.com/pages/Functional-Programming.html
Tomáš je autorem několika více či méně známých projektů jak z oblasti operačních systémů, tak internetu. V současnosti samozvaný expert na Linux, Bash, PHP a MySQL.
Přečteno 25 942×
Přečteno 23 974×
Přečteno 19 495×
Přečteno 18 281×
Přečteno 12 887×