Zabezpečení session proměnných

Školení, která pořádám

U složitějších webových aplikací je potřeba si uchovávat některé informace (např. informaci o přihlášení uživatele) mezi načteními jednotlivých stránek. K tomuto účelu byly navrženy cookies, které ale jsou samy o sobě v určitých případech snadno zranitelné. Pokud bychom si např. nastavili cookie identifikující přihlášeného uživatele, nic útočníkovi nebrání v tom, aby si hodnotu této cookie změnil podle potřeby. Cookies navíc mají velikostní limit (20 cookies na doménu, 4 KB na cookie), takže se nehodí pro ukládání velkých dat.

PHP má pro ukládání takovýchto informací vybudován mechanismus session proměnných, který cookies obvykle také používá, ale pouze pro uchování neuhodnutelného Session ID. I s tímto mechanismem jsou ale spojena určitá rizika. Na špatně zabezpečeném webovém serveru s více uživateli mohou ostatní uživatelé Session ID získat i s daty a i na lépe zabezpečeném serveru mohou kvůli chybě ve funkci glob získat alespoň Session ID (tato chyba je opravena v PHP 5.2.4). Z tohoto nebo jiného důvodu si možná budete chtít správu session proměnných vytvořit vlastní (která bude data ukládat např. do databáze). Pokud se tak rozhodnete, dá se s výhodou využít funkce session_set_save_handler, pomocí které si můžete snadno vytvořit vlastní mechanismus práce se session proměnnými se zachováním výhod standardních session – např. ponechání funkčnosti proměnné $_SESSION. Navíc bude kód pro jiného programátora snadněji pochopitelný.

Se správou session je spojeno několik různých útoků. Když někdo získá cizí Session ID a použije ho pro sebe, jedná se o Session Hijacking. Úniku Session ID na serveru lze zabránit jeho zabezpečením, na cestě a u klienta je to už horší. Cestu lze nejlépe zabezpečit používáním protokolu HTTPS místo HTTP, u klienta jsou naše možnosti velmi omezené (když uživatel např. sdílí adresář, do kterého se ukládají cookies, nebo používá nekvalitní prohlížeč, tak s tím nic nezmůžeme). Proto se jako doplňková ochrana používá např. kontrola IP adresy uživatele nebo jeho User-Agenta.

Dalším typem útoku je Session Fixation. Jedná se o útok, kdy útočník klientovi nastaví nějakou hodnotu Session ID (podstrčením odkazu na webu, v e-mailu nebo např. přímou editací uživatelových cookies) a jakmile se uživatel přihlásí, tak tuto Session ID použije pro sebe. Obrana proti tomuto typu útoku je v PHP poměrně jednoduchá – stačí před prováděním citlivých operací jako je přihlašování zavolat funkci session_regenerate_id, která způsobí změnu Session ID, takže původní ID útočník nebude moci využít.

Pokud uživatel nemá povolené cookies a je zapnutá direktiva session.use_trans_sid, přenáší se Session ID v URL. To sice umožňuje použít sessions i uživatelům s vypnutými cookies, ale přináší to další bezpečnostní rizika. URL se totiž ukládá na řadě míst (v historii prohlížeče, v logu serveru, jako Referer se dostane i na odkazované servery, …) a tím zjednodušuje útoky Session Hijacking. Kvůli útoku Session Fixation je vhodné mechanismus předávání Session ID v URL zcela vypnout direktivou session.use_only_cookies. Pokud session proměnné nepoužíváte k uchovávání citlivých informací, můžete tento mechanismus nechat zapnutý. Většina vyhledávačů se naštěstí snad již naučila ignorovat session identifikátory používané PHP, takže stránky s různými Session ID se ve vyhledávačích neukládají víckrát, v cachích ale pochopitelně pořád ano.

Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací (14.9.2016, Praha).

Jakub Vrána, Seznámení s oblastí, 1.7.2005, diskuse: 23 (nové: 0)

Diskuse

Naith:

Dobrý den, tento článek je poněkud starší, ale snad mi někdo  poradí. Při práci na jednom projektu jsem narazil na zákaznika, který měl vypnuté cookies. Pokud jsem je tedy použil pro obsloužení session, tak mu to pochopitelně nefungovalo. Trval na tom, že se dočetl o nebezpečnosti cookies a proto je nezapne. Nevim jak udržet konzistentnost session, anichž bych nepřenášel session v url. Nemohu přeci na každy odkaz použít post metodu formulářů.

ikona Jakub Vrána OpenID:

Metodou POST posílá kontextová data třeba ASP.NET, u obyčejných odkazů to řeší JavaScriptem. Toto řešení mi je krajně protivné.

Zákazníkovi vysvětlete, že jediná rozumná alternativa k přenášení session ID v cookie je přenášení v URL, které je mnohem nebezpečnější. Cookies se dají zneužít především ke sledování pohybu člověka pomocí různých počítadel, ale k tomu stačí zakázat 3rd party cookies, prohlížeče na to mají oddělené nastavení.

dzabza:

Dobrý den , Jsem úplný začátečník a pokouším se vytvořit stránku jen html. Rád bych ale na určitou stránku vložil registraci - jméno a heslo pro vstup. existuje nějaký zdroj.kod , který jen vložím a bude to fungovat? Nevím také jak funguje MySQL nebo PHPadmin , které mi server umožnuje  pokud to stím souvisí a dá se to nějak propojit.
Pokud by mi někdo poradil , jak to mám vše nastavit nebo co mám kam vložit. Čtu to pořád dokola a jsem asi natvrdlej.
Díky.

PeTa:

Myslím, že nejrozumnější by bylo aby sis koupil nějaké knihy a pokusil se pochopit, jak daná věc funguje. Určitě bys nekde na internetu našel kousek kódu, který by dělal to, co očekáváš, ale to je úplně to nejhorší, co můžeš udělat. Jako bys sedl do raketoplánu a prohlásil ukažte mi páčku za kterou mám tahat a já poletím. Poletíš, ale tak maximálně do p...

Thomas:

Vzdy kdyz vidim podobny prizpevek vybavi se mi me vlastni zacatky plne zoufalstvi z toho ze do niceho z toho co bych chtel zrealizovat vubec nevidim. Lepil jsem veci, z dnesniho pohledu, naprosto obludnym zpusobem. Reseni je bohuzel jedine, a nelze ho zadnou fintou obejit : Zacit se ZODPOVEDNE UCIT vsechno od zacatku. Specifikace W3C - XHTML, CSS, Javascript+DOM, PHP, SQL (metody navrhu databazi, obsluha case nastroju) ... tohle by mely byt temata, na ktere bys mel ted uprit veskerou svoji studijni pili :-) Drzim palce.

Thomas:

Druha moznost, je nechat si to vypracovat nekym kdo si tim uz prosel :-)

Silvestr:

Když zůstanem u toho srovnání, tak v tomhle případě místo raketoplánu zřejmě stačí řetízkový kolotoč - soubor .htaccess .
1) Nevím, jestli tu o jeho použití má něco Jakub Vrána (asi jo); vím, že pár řádek je na Jak psát web.
2) Vygooglujte "htpasswd generator", abyste získali dvojici login:zakódované heslo.
Nedoletíte s tim na Orbit, ale ve vzduchu chvíli budete.

Pacek:

Když bych zašel do extrému: při Session Hijacking je teoreticky možné mít jako útočník korektní IP (např na pracovišti nebo ve škole) i User_agent, existuje nějaká 100% ochrana proti Session Hijacking ?

ikona Jakub Vrána OpenID:

Obranou je nenechat uniknout session identifikátor: Ten se útočník může dozvědět jednak na serveru, z URL, cookies nebo z přenášených dat. Je tedy potřeba ochránit data na serveru, v URL session identifikátor nejlépe nepřenášet, nedopustit XSS a nejlépe i šifrovat přenášená data. Více si o tom můžete přijít popovídat na školení o bezpečnosti.

ikona Marty:

Mám jednu otázku, která je off-topic. Pokud pracuji se sessions a uživatel se přihlásí na: www.neco.cz, tak není problém, pokud však přejde na obchod.neco.cz, tak už přihlášený není, je to zřejmě proto, že cookie se session identifikátorem tam neplatí, nevíte někdo jak nastavit tuto platnost, aby platila na všechny subdomény (kompletní web)? Děkuju za odpověď

ikona Jakub Vrána OpenID:

Konfigurační direktiva session.cookie_domain nebo parametr $domain v session_set_cookie_params().

Kamil:

Dobrý den, když je řeč o zabezpečení, chci se zeptat na váš názor na jeden způsob zabezpečení, který na svých stránkách používám já. Uživatel se přihlašuje pomocí svého uživatelského jména a hesla, oboje je uloženo v databázi MySQL, heslo zašifrováno pomocí MD5. Pokud se jméno a zašifrovaná hesla rovnají, uloží se do databáze k těmto údajům (k údajům uživatele) také IP, z které se přihlásil, čas, a také jistý kód, který je tvořen zřetězením těchto dvou údajů a zašifrováním pomocí MD5:

<?php
$time
=time();
$kod=MD5($time.$REMOTE_ADDR);
?>

poté se vytvoří řetězec $url, který je předáván metodou GET v adrese po přechodu na jinou stránku mého systému:

<?php
$url
="id=".$aa[id]."&code=".$kod;
?>

po přechodu na jinou stránku se zkontroluje, jestli se kód rovná s kódem v databázi, jestli se rovná IP uživatele po přechodu na jinou stránku s IP v databázi, a jestli rozdíl časů není větší než povolené maximum.

Můžu říct, že mé stránky jsou zabezpečené, nebo se tento systém dá nějak obejít? Pro ukázku přikládám celý zrojový kód ověřování uživatele. Tento kód je vložen na KAŽDOU stránku a poté jsou na každé stránce dva bloky:

<?php
if ($prihlasen=="")
{
echo
'jste prihlasen';
}
else
{
echo
'nejste prihlasen';
}
?>

OVĚŘOVACÍ KÓD:
<?php
$prihlasen
="";

$id=$_GET['id'];
$code=$_GET['code'];

// ODHLÁŠENÍ UŽIVATELE
if($co=="logout")
    {
    if($id=="")
        {
        $error="<b>Nejste přihlášen";
        }
    else
        {
        MySQL_Query("UPDATE $MySQL_tabulka_admin SET ip='' WHERE id='$id';");
        $code="";
        $error="Nyní jste byl bezpečně odhlášen.";
        }
    }

//KONTROLA PŘIHLAŠOVACÍCH ÚDAJŮ

if($_POST["nick"]!="")
    {
    $bb = MySQL_Query("SELECT * FROM $MySQL_tabulka_admin WHERE id=1;");
    echo MySQL_Error();
    $aa=MySQL_Fetch_Array($bb);
    $heslo=MD5($_POST["heslo"]);
    if(($aa[heslo]==$heslo) and ($heslo!=""))
        {
        $IP=$REMOTE_ADDR;
        MySQL_Query("UPDATE $MySQL_tabulka_admin  SET ip='$IP' WHERE id=1;");
        $time=time();
        MySQL_Query("UPDATE $MySQL_tabulka_admin  SET posledni_akce='$time' WHERE id=1;");
        $kod=MD5($time.$REMOTE_ADDR);
        MySQL_Query("UPDATE $MySQL_tabulka_admin  SET kod='$kod' WHERE id=1;");
        $url="id=".$aa[id]."&code=".$kod;
        $prihlasen=$aa[id];
        $id=$aa[id];
        }
    else
        {
        $error="Přihlášení se nepodařilo. Špatné heslo";
        }
    }
//KONTROLA PŘIHLÁŠENÍ (DOBA NEČINNOSTI, KONTROLA IP)
if($code!="")
    {
    $bb = MySQL_Query("SELECT * FROM $MySQL_tabulka_admin  WHERE id='$id';");
    $aa=MySQL_Fetch_Array($bb);
    $rozdil = time() - $aa[posledni_akce];
    if($rozdil>900 or $rozdil<0) $povol="ne";
    if(($aa[kod]==$code)and($aa[ip]==$REMOTE_ADDR) and ($povol==""))
        {
        $time=time();
        MySQL_Query("UPDATE $MySQL_tabulka_admin  SET posledni_akce='$time' WHERE id='$id';");
        $url="id=".$id."&code=".$code;
        $prihlasen=$id;
        }
    else
        {
        $str="";MySQL_Query("UPDATE $MySQL_tabulka_admin SET ip='' WHERE id='$id';");
        $error="Nejste přihlášen.<BR>Buď nesouhlasí IP, nebo se provedlo automatické odhlášení, kvůli neaktivitě delší než 10 minut. Přihlaste se proto prosím znovu.<br><br>";
        }
    }
?>

Byl bych moc vděčný za váš názor.

hook:

kdyby jsi použil session tak místo těchto x řádků ti bude stačit jeden ( možná dva ).

ikona EForce:

no ale hlavně není bezpečné někde svůj kód takto zobrazovat si myslím. čím méně informací o kódu bude útočník mít tím lépe pro nás...

ikona v6ak:

To je sice pravda, ale na druhou stranu by dobrý kód neměl jít napadnout ani když jej útočník zná.

Squad_leader:

Dobrý den,

"20 cookies na doménu, 4 KB na cookie"

Nemáte tak docela pravdu.
Toto platí u InternetExploreru verze 6.
U moderních prohlížečů toto omezení neexistuje (alespoň co do počtu) či je nastaveno na větší hodnotě.

ikona Jakub Vrána OpenID:

Nemáš tak docela pravdu ty, takto hovoří norma. Pokud chceš udělat něco záslužného, můžeš skutečné limity v ostatních prohlížečích zjistit a uvést je sem. Když jsem to ale naposledy zjišťoval já, tak to zhruba odpovídalo.

Ale smysl tohoto počínání je zanedbatelný, protože v aplikaci je stejně potřeba počítat s nejmenší možnou hodnotou v podporovaných prohlížečích, která shodou okolností navíc odpovídá normě.

Prdlořeznictví Krkovička, n. p.:

aby se prohlížeč mohl honosit nálepkou "podporuje cookies", pak musí podporovat:
* alespoň 300 cookies celkově
* alespoň 20 cookies na doménu
* alespoň 4096 bajtů na cookie (data)
* pokud prohlížeč nemůže přijmout větší cookie, nesmí jeho data "oříznout" ale nepřijmout jako celek

http://tools.ietf.org/html/rfc2965

Naj:

Zdar lidi,
nešlo by se proti Session Hijacking částečně bránit i častou změnou Session ID tzn. volaním funkce  session_regenerate_id? Jak myslíte, že by to bylo úspěšné v praxi ?

pojízdná kočka:

Otázka ohledně session.use_only_cookies:
Case study: Je webové místo - e-shop, který má několik málo registrovaných zákazníků, kteří se na tyto stránky přihlašují. Když zakážu přístup k session jiným způsobem než přes cookie, možná předejdu některým bezpečnostním; ale též možná "odříznu" uživatele, kteří mají cookies vypnuty, nebo jejich prohlížeče cookies nepodporují (je sice rok 2009, na druhou stranu jsem masírována 'doktrínou webové přístupnosti'). Doporučili byste mi jiné přístupy k session než přes cokie vypnout?

Jakub:

Potřebuju v odkazu na tu samou stránku změnit session proměnnou. Nějak mně to nejde.

Daniel:

Ahoj, potřeboval bych poradit, trošku jsem si hral se session
a nastavil jsem si session.use_trans_sid a teď se mi za URL objevuje toto: &PHPSESSID=a7038e56cc934480428cce47bf6383f9 vím co to je, ale nedaří se mi to vypnout. poraď te mi prosím jak to mám vypnout a vrátit zpět do původního stavu. Díky

Pavel:

Ahoj mám takový problém se SESSION. Normálně mi vše funguje, pouze když přijdu na stránky tak mi blbne přihlášení dokud se ručně neodhlásím, pak se jde teprve přihlásit. poradil by mi někdo? díky

Vložit příspěvek

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2016 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.