Bezpečnost webového serveru

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

PHP je stejně jako většina projektů s otevřeným zdrojovým kódem poměrně bezpečné. Díky otevřenému kódu si s vynaložením určitého úsilí může každý ověřit, jestli v jazyce nejsou otevřená nějaká zadní vrátka nebo se v něm neskrývají jiné bezpečnostní hrozby. Nebo se dá zkrátka věřit tomu, že těch několik desítek lidí, kteří se ve zdrojáku jazyka PHP vyznají a sledují všechny změny, si vzájemně nekryje záda. Díky otevřenému kódu je také obvykle mnohem rychlejší vydávání bezpečnostních záplat.

Úzké hrdlo bezpečnosti serveru tedy obvykle netkví přímo v PHP, ale spíše v nastavení přístupových práv, obzvláště na serverech s více uživateli. Abych to trochu vysvětlil, je nutné nejprve potřeba stručně popsat, jak to na serverech s více uživateli vypadá. Jednotliví uživatelé obvykle dostanou vlastní přístupové údaje, pomocí kterých si na server nahrávají své soubory. Zobrazování souborů na webu zajišťuje webový server, který obvykle běží pod vyhrazeným uživatelem (platí např. pokud je PHP použito jako modul Apache). Aby soubory mohly být zobrazeny, musí mít tento uživatel právo jejich čtení. A v tom tkví bezpečnostní riziko – když se pod tímto uživatelem provede jakýkoliv kód, má práva čtení všech souborů zobrazovaných na webu. Může si tedy např. přečíst zdrojové kódy ostatních webů, přístupové údaje a cokoliv dalšího ho napadne.

Přestože se tento problém netýká přímo PHP (úplně stejný problém nastane i při použití jiných skriptovacích technologií, takže by měl být řešen spíše na úrovni webového serveru), nabízí proti němu PHP poměrně účinnou obranu. Direktiva open_basedir slouží k zamezení přístupu mimo zadaný adresář. Pokud tuto direktivu správně nastavíme např. pro každou doménu, máme skoro vyhráno. „Skoro“ píšu proto, že PHP samo o sobě nezaručuje, že všechna rozšíření budou tuto direktivu respektovat – nicméně pokud používáme pouze ta standardně dodávaná, spolehnout se na to v podstatě dá. Poslední problém tak zůstává u funkcí pro spouštění příkazů systému jako je např. exec. Ty buď můžeme zakázat direktivou disable_functions nebo spolu s dalšími omezeními omezit direktivou safe_mode.

Na některých hostinzích je zapnuta jenom direktiva safe_mode a jejich správci žijí v přesvědčení, že je jejich server neprůstřelný. Tato direktiva mimo jiné ověřuje, jestli vlastník načítaného souboru odpovídá vlastníkovi spuštěného skriptu. Nic nám ale nebrání si vlastním skriptem vytvořit jiný skript – ten už bude patřit uživateli, pod kterým běží webový server, takže z něj následně bude možné přečíst všechny stejně vytvořené soubory – např. soubory se session nebo veškerá data nahraná na server PHP skriptem a uložená do souborů.

<?php
// skript bad_code2.php bude patřit uživateli, pod kterým běží webový server
copy("bad_code.php", "bad_code2.php");

// tímto skriptem si např. můžeme přečíst všechny soubory s uloženými session
$session_path = preg_replace('~.*;~', '', ini_get("session.save_path"));
foreach (glob(($session_path ? $session_path : $_ENV["TEMP"]) . "/sess_*") as $filename) {
    echo "$filename\t";
    readfile($filename);
    echo "\n";
}
?>

Pokud tedy provozujete webový server, na který má přístup více uživatelů, tak alespoň správně nastavte obě direktivy – open_basedir a buď safe_mode nebo disable_functions. Zamezit získání názvů session proměnných je nejlepší přímo na úrovni operačního systému – stačí adresář session.save_path učinit nečitelný pro uživatele, pod kterým běží webový server (a pokud mají na server přístup i běžní uživatelé, tak samozřejmě i pro ně). Tento uživatel pak bude moci v adresáři číst a zapisovat soubory, ale nepodaří se mu získat jejich seznam.

Na tom se ostatně bezpečnost webového serveru dá také vystavět – stačí weby jednotlivých uživatelů umístit do adresářů neuhodnutelného jména, nadřazenému adresáři sebrat právo čtení a obejdete se bez open_basedir i safe_mode. Pokud si ale uživatel zapne direktivu display_errors, tak si při chybě skutečný adresář ukáže – i proto je dobré mít tuto direktivou na ostrém webu vypnutou.

Podotýkám, že tento text se vztahuje k bezpečnosti webového serveru jako takového. Další důležitou oblastí je zabezpečení webové aplikace, kde toho v PHP jde zkazit ale také samozřejmě ošetřit většinu.

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

Jakub Vrána, Dobře míněné rady, 17.6.2005, diskuse: 15 (nové: 0)

Diskuse

T3RMiX:

Výborný článek, jen tak dál! :)

Leo:

Krome toho je dobre podivat se, kde je docasny adresar pro uploadovane soubory, a kde pro session (pokud je stejny pro vsechny virtualni servery neni to to prave orechove), Leo

The Zero:

Existující sessions se dají i přepsat, stačí otevřít, zapsat, zavřít.

Cyril:

Dobré vědět. Díky.

karol:

super clanok..

Štěpán Svoboda:

Ahoj

Jsem tady trochu s křížkem po funuse ale zkoušel někdo vymyslet způsob jak použít direktivu open_basedir při použití virtuálních hostitelů?

httpd.conf:

VirtualDocumentRoot /data/domains/%-2.0.%-1/subdomains/%-3

Štěpán Svoboda:

Pokud jsem prostudoval dostupné materiály dobře - zdá se mi že to totiž nejde. Nebo zná někdo nějakou fintu. Jediný způsob jak použít tuto direktivu je zřejmě každého jednotlivého hostitele explicitně definovat v kontejneru <VirtualHost>.

ikona Jakub Vrána OpenID:

Ano, takhle se to běžně dělá.

Srigi:

No ako pomocou tejto konstrukcie zaistim pre kazdy virtual-host jeho jedinecne nastavenie PHP open_basedir?

To ide spustit PHP v niekolkych instanciach, alebo je napr. mozne PHP conf direktivy nastavovat v httpd.conf?

ikona Jakub Vrána OpenID:

PHP direktivy lze nastavovat v httpd.conf: http://www.php.net/manual/en/configuration.….changes.apache

Srigi:

Super, tak snad uz nebude dovod sa bat doma si "vystrcit" Apache2 do Internetu.

kozotoč:

koukam se do dokumentace php na "safe mode" a vidim, ze od PHP v.6.0 bude (nebo u nekoho uz je) vypnuty. jeste nevim, jak to pani vyvojari vyresili pro 6.0 a vyssi verze (treba ten -podle jejich vlastnich slov- nekorektni pristup opustili a delaj to uplne jinak)

Viktor:

Cituji: "tak alespoň správně nastavte obě direktivy – open_basedir a buď safe_mode nebo disable_functions."

Nejak nechapu, jak spravne nastavit disable_functions. Pomoci teto direktivy se, pokud je mi znamo, daji pouze natvrdo nektere primo vyjmenovane funkce zcela znefunkcnit. Ale tomu bych nerikal spravne nastaveni. Vzdyt ty funkce tvurci jazyka PHP nevymysleli apriori jako bezpecnostni hrozbu, ale proto, ze se mohou v nekterych skriptech hodit k pachani neceho pozitivniho. Kdyz je zakazu, ano je to reseni jednoduche, rychle, ucinne, ale spousta veci mi tak najednou nepojede a neni nahrada za ne. To mi tedy jako reseni neprijde. A safe_mode, to je prezitek, ktery uz stejne nebude dale podporovan. Takze je to cele docela pruser a nejake ciste reseni 100% bezpecne, pritom normalni aktivity neomezujici, nejspise vubec neexistuje. Tedy ne na urovni PHP/Apache bez instalovani spec. modulu do Apache.

ikona Jakub Vrána OpenID:

Měl jsem na mysli zakázání funkcí pro spouštění programů příkazového řádku. Pokud jsou potřeba, tak se toto řešení samozřejmě použít nedá, ale potřeba by být neměly, protože téměř na vše v PHP existuje extenze a na co ne, na to se dá obvykle vytvořit.

Řešení nezávislé na konfiguraci PHP a modulech webového serveru popisuji na svém školení http://php.vrana.cz/skoleni-bezpecnost-php-aplikaci.php. Časem ho popíšu i na tomto blogu.

Viktor:

Ano, ja to pochopil presne tak, jak jste to myslel. Proste system, passthru apod. Ale pokud se budeme bavit o nejbeznejsim prostredi sdileneho webhostingoveho serveru, tam asi uznate, ze zakaz techto funkci okamzite zapricini prskani zakazniku - ja bych rekl, ze leckdy opravnene.

Pokud existuje nejake reseni v PHP, ktere by umoznovalo mit vypnute vsechny tyto funkce na spousteni programu z prikazoveho radku a presto si nejakou obezlickou spustim napr. bashovy skript, tak to mi bohuzel do ted zustalo zcela utajeno.

Presto, i kdyby to bylo zrejme mne, ale ne zakaznikum a jejich webmasterum (ci webmasterum ruznych free CMS reseni), stejne by ty standardni funkce musely byt povoleny.

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.