Poor Http a Poor Publisher implementují
PoorSession. Jde o samonosnou cookie, do které může webová
aplikace ukládat různá data. Tento mechanismus je sice omezený
svou velikostí, nicméně k běžnému testování, zda je uživatel
přihlášen nebo si zapnul nějaký speciální režim, vyhovuje
dostatečně.
3.8.2011 00:00 | Ondřej Tůma | přečteno 8118×
„Samonosná cookie – je
taková cookie, ve které jsou uložena všechna data aplikace a
není třeba dalšího úložiště dat. Proto jsou tyto data
kryptována, aby je uživatel nemohl nijak
podvrhnout.“
Celá implementace spočívá v triku, kdy jsou data
serializována, zaheslována, zkomprimována a nakonec převedena
na byte64. Opačnými kroky jsou pak data získána zpět. V
datech, ve slovníku, je také uvedena expirace session, takže
i v případě, že útočník podvrhne stará data, aplikace je
zahodí. Klíč kterým jsou data kryptována a následně
de-kryptována je samozřejmě konfigurovatelný, ve výchozím
stavu jde o identifikátor ze svn commitu příslušného souboru
(informace o poslední úpravě souboru na SF.net).
Vytvoření a zrušení session
Následující funkce doLogin vytvoří novou session. Do jejího
datového slovníku uloží id uživatele, časovou známku a pokud
vstupní proměnná ip není nastavena na None, False, 0 nebo
prázdný string, nastaví také ip adresu klientské aplikace.
Následně nastaví hlavičky odpovědi, čímž zajistí, že se tato
session dostane do klientské aplikace – browseru. Ve
skutečnosti nevytvoří novou session, ale použije již
existující session, kterou poslal browser serveru společne s
požadavkem.
def doLogin(req, id, ip = None):
cookie = PoorSession(req)
cookie.data["id"] = id
cookie.data["timestamp"] = int(time())
if ip:
cookie.data["ip"] = req.get_remote_host()
cookie.header(req, req.headers_out)
req.log_error("Login cookie was be set.", LOG_INFO)
#enddef
Funkce doLogout naopak rovnou předpokládá, že společně s
požadavkem poslal browser i cookie ve které je uloženo id
přihlášeného uživatele. Pokud tomu tak je, zavolá metodu
destroy, která vymaže data, a nastaví session záporný čas.
Nakonec opět zapíše vytvořenou cookie do hlavičky odpovědi.
Díky tomu pak browser získá informaci že cookie může smazat.
I kdyby tomu tak nebylo a browser ji sám posílal dál, nebude
již obsahovat žádná data.
def doLogout(req):
cookie = PoorSession(req)
if not "id" in cookie.data:
req.log_error("Login cookie not found.", LOG_INFO)
return
cookie.destroy()
cookie.header(req, req.headers_out)
req.log_error("Login cookie was be destroyed (Logout)", LOG_INFO)
#enddef
Práce se session
Naproti tomu, se funkce checkLogin pokouší získat Session z
cookie. Pokud je v pořádku a obsahuje id přihlášeného
uživatele a pokud má nastavenou správnou ip adresu klienta,
vrátí tuto session. V opačném případě zaloguje chybu,
případně smaže session a vrátí None. Volitelným parametrem je
redirectUri, ten pokud je nastaven a funkce vyhodnotí situaci
záporně, dojde k přesměrování na jinou adresu. Pod takovou
adresou, pak může server vracet chybovou hlášku o nutnosti
přihlášení, přihlašovací formulář atd.
def checkLogin(req, redirectUri = None):
cookie = PoorSession(req)
if not "id" in cookie.data:
req.log_error("Login cookie not found.", LOG_INFO)
if redirectUri:
redirect(req, redirectUri)
return None
if "ip" in cookie.data and cookie.data["ip"] != req.get_remote_host():
cookie.destroy()
cookie.header(req, req.headers_out)
req.log_error("Login cookie was be destroyed (invalid IP address)",
LOG_INFO)
if redirectUri:
redirect(req, redirectUri)
return None
return cookie
#enddef
Použití těchto funkcí může být například stejné, jako je v
následující ukázce kódu. Důležité je, že aby se data uložená
v session neztratila, je nutné je zapsat do hlavičky
odpovědi. Browser pak dle nastavení tuto cookie smaže, nebo s
dalším požadavkem pošle na server.
# kontrola zda je uživatel přihlášen
session = checkLogin(req, "/login")
# inkrementování hodnoty uložené v session
session.data['count'] = session.data.get('count', 0) + 1
# uložení session do klientského browseru
session.header(req, req.headers_out)
# registrace nové session (přihlášení)
doLogin(req, 'x', True)
http.redirect(req, "/")
# odstranění session (odlášení)
doLogout(req)
http.redirect(req, "/login")
Veškerá práce se session je jasně limitovávána prací s
cookie. To znamená, že pokud nedojde k jejímu přepsání ze
strany serveru, klient ji bude vždy posílat automaticky,
dokud nevyprší. V případě testování přihlášení, kdy nedochází
k žádnému zápisu, resp. změně, není třeba ji s každou
odpovědí posílat, tedy generovat hlavičky. Navíc, třída
PoorSession vytváří novou session jak v případě, kdy server s
požadavkem žádnou cookie nedostal, tak i v případě kdy došlo
k její expiraci nebo z nějakého důvodu není čitelná.
Expirace je implicitně nastavena na 0, to znamená, že k
jejímu zneplatnění dojde až se rozhodne browser, to znamená
při zavření. Tato hodnota je pro kontrolu uvedena také v
datové části session pokud je nastavena. Je tedy silně
nedoporučováno nepoužívat klíč „expires“ v session slovníku
data, protože při
nastavení bude tato hodnota přepsána.
Dalším rozšířením by mohlo být zápis na filesystém serveru,
nebo do nějakého dalšího úložiště, například databáze. V
takovém případě je ale nutné do systému zahrnout nějaký
systém na mazání již zastaralých session. Protože ty jsou
předem nespecifikovatelně trvalé a jsou ukládány na straně
klienta, je použití právě samonosné cookie elmi výhodné a
velmi často dostačující.