FPD je jedna z přibližně 17576 třípísmenných zkratek používaných na Internetu a jedna z mála, kde písmeno F neznamená, hmm, třeba friend. Význam zkratky, o kterém bych vám rád povyprávěl je však důležitý pro bezpečnost webových aplikací. FPD totiž v oblasti webové bezpečnosti znamená Full Path Disclosure, do češtiny přeloženo například jako odhalení, nebo raději lépe prozrazení úplné cesty.
Cestou je myšlena cesta k souboru, k právě spuštěnému skriptu, ne cesta domů z vašeho oblíbeného baru.
Pokud něco podobného uvidíte na vlastním webu, pak váš web neodolá útoku Full Path Disclosure a plnou cestu vyzradí. A to by rozhodně neměl. Už vidím, jak se vaše dlaň blíží k vašemu obličeji a jak si říkáte, že cestu ke svým souborům přece znáte a jak nechápavě kroutíte hlavou. Zobrazená informace je totiž důležitá nejen pro vás, ale i pro zlé hochy, kteří chtějí na váš web nějakým způsobem zaútočit.
Ze zobrazené chybové hlášky lze kromě plné cesty ke skriptu search.php
vyčíst i pár dalších důležitých informací:
strtolower()
očekává řetězec jako první parametr (to kdybyste to náhodou zapomněli)Jen tak mezi námi, i ta samotná plná cesta je důležitá, na některých serverech totiž cesty neodpovídají názvům domén a soubory jsou uloženy v adresářích s generovanými názvy (například /www/sites/8/site13148/public_html
je cesta k souborům webu adriadatabanka.com) a tak se může stát, že sami nevíte, kde soubory vlastně máte. Full Path Disclosure vám je tedy pomůže najít (a nejspíš proto ani půl roku po nahlášení tato zranitelnost není opravena). A pomůže je najít nejenom vám, ale všem, kdo je najít chtějí, tedy i případným zlým hochům.
Někdy ze zobrazených informací lze vyčíst i verze použitých nástrojů a stack trace, tedy kompletní seznam souborů a funkcí, které se volaly do té doby, než došlo k chybě (třeba u Chrise Shifletta, autora knihy Essential PHP Security, na jeho Not Found stránce) nebo to, jaký databázový server a jaké rozšíření se používá pro přístup a jak je odesílání dotazů ošetřeno, to když uvidíte hlášku podobnou této:
mysql_real_escape_string() expects parameter 1 to be string, array given
Zkrátka Full Path Disclosure útok může vyzradit spoustu informací, které lze pak využít k plánování a provádění dalších útoků s mnohem větším a horším dopadem, jako třeba SQLIA (SQL Injection Attack, to je když vám někdo z databáze vyláme uživatelská jména a hesla) nebo LFI (Local File Inclusion, to je když vám někdo stáhne soubor, který nemá stahovat, třeba konfigurační soubor s přístupovými údaji k databázovému serveru). Tento útok by se tedy dal nazvat lépe a obecněji jako FPIADHYB (Full Path, Information and Architecture Disclosure, Hell Yeah, Baby). V otázkách bezpečnosti jsem se naučil zobecňovat a vidět svět černobíle a proto vždy říkám, že útok Full Path Disclosure je stejně závažný, jako třeba zmíněné SQLIA nebo LFI.
Full Path Disclosure je poměrně běžný útok a často je i úspěšný (a občas se dotkne i kováře a jeho kobyly nebo webu, který je napsán v nějakém známém frameworku), je proto dobré vědět, jak se dá takový útok provést, to abychom se proti němu dokázali efektivně bránit. Pokud se budete snažit vyrobit útok FPD na váš vlastní web (na cizí weby přeci neútočíme, víme?), tak vězte, že fantazii se meze nekladou, základem je vyvolat nějakou chybovou hlášku PHP, která standardně obsahuje i plnou cestu k souboru, kde k chybě došlo. Zde je pár základních triků, které vám pomůžou v experimentování.
Pokud za název parametru přidáte prázdné hranaté závorky, PHP ze vstupního parametru udělá pole. S tímto trikem skript ovšem nepočítá a tuto proměnnou v klidu předá funkcím, které pracují pouze s řetězci (například strtolower()
, nebo preg_match()
) a když se jim předá pole, tak zahlásí chybu. Příkladem budiž třeba search.php?q[]=…
, výsledná chyba je pak zachycena na úvodním obrázku. Ve formulářích odesílaných metodou POST pak stačí pomocí nástrojů vašeho prohlížeče změnit název INPUT prvku přidáním hranatých závorek například na name="search[]"
.
Identifikátor session má jistá omezení, co se týká povolených znaků a jejich počtu. Nepovoleným znakem je třeba mezera a nepovolený počet znaků je třeba nula, tedy prázdný řetězec. Pokud identifikátor s nepovoleným znakem nebo nepovolenou délkou použijeme, session_start()
zahlásí chybu. Identifikátor session se dá změnit například přímo ve vašem prohlížeči (hledejte cookie s názvem PHPSESSID
nebo něco podobného), případně jej můžete změnit pomocí JavaScriptu vložením následujícího kódu do řádku s adresou v prohlížeči ve chvíli, kdy máte načten váš web a následným reloadem stránky.
javascript:void(document.cookie="PHPSESSID=");
Pokud web přenáší identifikátor session v URL (což by dnes už žádný moderní web neměl dělat), tak není nic jednoduššího než změnit daný parametr v URL.
Zkuste si v prohlížeči přímo zavolat soubory, které pouze vkládáte (includujete) do jiných souborů. Je možné, že uvidíte hlášku podobnou této:
Fatal error: Call to undefined function login() in /stor1/vemex/html/form.php on line 7
Ano, i v tomto případě váš web právě neodolal FPD útoku.
Pokud skript vyžaduje nějaké parametry, zkuste si ho zavolat bez těchto parametrů. Možná budete překvapeni.
Zkontrolujte, že všechny vaše veřejně dostupné skripty mohou číst soubory, které jsou potřeba k jejich úspěšnému vykonání.
Pokud nějaký váš skript zpracovává HTML nebo XML, zkuste mu poslat well-deformed data, nebo něco, co jako HTML nebo XML moc nevypadá, třeba se mu to nebude líbit.
Pokud je na webu nějaký skript, který přesměrovává, zkuste se nějakým speciálním nástrojem (Firebug, Fiddler, curl) podívat na jeho výstup, když budete zkoušet výše popsané triky. Je totiž možné, že skript prozradí plnou cestu a další informace, ale poté přesměruje kamsi do pryč, takže v konvenčním prohlížeči očekávanou chybovou hlášku neuvidíte.
Spousta skriptů typu system check toho taky vyzradí spoustu a to nemusí ani hlásit žádnou chybu. Do této skupiny patří i všechny ty /info.php
, /phpinfo.php
, /pi.php
apod. skripty, tedy výstup z funkce phpinfo()
. Ta je nebezpečná i proto, že z jejího výstupu lze získat session id i když cookie, ve které se přenáší, má atribut HttpOnly
.
Můžete se snažit sebevíc, ošetřovat různé stavy a typy proměnných a vůbec všechno dělat a psát defenzivně, ale milé PHP vás stejně vždy něčím překvapí. Jediná možnost je tedy vypnout zobrazování chybových hlášek pomocí direktivy display_errors
. Nejlépe už přímo v konfiguraci serveru, když to budete dělat až někde ve skriptu, tak už může být pozdě. V souboru .htaccess
to uděláte takto:
php_flag display_errors off
Chyby tedy nebudou zobrazovány, ale rozhodně je dobré, abychom o nich věděli. Měli bychom je tedy někam ukládat, nejlépe do nějakého logu. To se zařídí direktivou log_errors
, v souboru .htaccess
takto:
php_flag log_errors on
Do jakého logu se budou chybové hlášky ukládat určuje direktiva error_log
. Zkuste se podívat třeba do serverového error logu.
Rozhodně nikdy nevypínejte zobrazování chybových hlášek tím, že zakážete jejich generování například pomocí error_reporting(0);
, tím zakážete i jejich ukládání do logu. Kde není chybová hláška, tam totiž není co ukládat.
Nutno poznamenat, že FPIADHYB není jen záležitost PHP, ale týká se v podstatě všech technologií, které nějakým způsobem vypisují chybové hlášky obsahující užitečné informace.
TL;DR: nastavte display_errors = off
Přijďte si o FPIADHYB, SQLIA, LFI a BBQ popovídat na školení bezpečnosti PHP aplikací (nejbližší termín: termín zatím nevypsán) nebo, pokud jste dobří v odbíhání od původního tématu, klidně i na jakékoliv jiné školení.
Disclaimer: mám za to, že odkazovaní o problémech vědí, jen je nezajímají. Jinak si totiž nedovedu vysvětlit to, že i po několika měsících od nahlášení nejsou vyřešeny. Pokud odkazovaní o problémech nevědí, tak jim, milí čtenáři, dejte vědět a pošlete jim odkaz na tento článek. Děkuji.
Text jsem napsal už v roce 2012 pro DevBlog Borka Bernarda, v roce 2024 jsem jen doplnil odkazy na již neexistující stránky, které nedlouho po původním vydání stihl uložit Internet Archive, opravil překlepy a na svůj web umístil v původním znění.