PHP (92) - funkce pro interakci s operačním systémem

Může PHP spouštět systémové příkazy? A jestliže ano, tak jak?

17.1.2005 15:00 | Petr Zajíc | přečteno 34086×

Ačkoli PHP se v naprosté většině případů používá k tvorbě dynamických webových stránek, je dobré vědět, že může být použito i jinak. V dnešním díle se zamyslíme nad  způsoby, jakými může programátor pomocí PHP zasahovat do operačního systému.

Utajený prováděcí operátor

Prováděcí operátor je obrácený apostrof (`). Nepleťte si jej s klasickým apostrofem ('). Na tomto operátoru není nic tajného, nazval jsem ho tak prostě proto, že ho většina lidí vůbec nezná. Ve skriptu tahle potvůrka způsobí, že příkaz, který je uzavřený v obrácených apostrofech PHP ignoruje a je předán k provádění operačnímu systému. Takže můžete například napsat:

echo `ls`;

Když se to spustí, provedou se následující věci:

V praxi si to budete moci vyzkoušet málokde. Na naprosté většině webhostingů je obrácený apostrof zakázán, protože administrátor bude stěží chtít, abyste pomocí PHP jakkoli manipulovali s hostitelským počítačem. Může se to ale hodit například při vývoji intranetových aplikací, kde to za určitých podmínek může být relativně bezpečné.

Pokud budete z nějakého důvodu chtít nebo muset používat prováděcí operátor, měli byste pamatovat na následující věci:

  1. Omylem nebo úmyslně tak můžete vytvořit skripty, které jsou nepřenositelné na jinou platformu. Protože například Windows nemají příkaz ls, příklad výše by tam nefungoval.
  2. Pokud potřebujete nejen spouštět akce na serveru, ale rovněž zpracovávat výstupy z těchto akcí pamatujte, že si můžete pěkně naběhnout. Výstup může být totiž formátován jinak, než byste snad očekávali, či může být v jiné znakové sadě.
  3. Jestliže spustíte příkaz, který by normálně očekával uživatelský vstup, PHP se může jevit jako zaseklé, protože spuštěný program nebyl ukončen a PHP čeká na výsledky.
  4. Jestliže stránka obsahuje prováděcí operátor a v prohlížeči zrušíte její načítání, neznamená to automaticky, že spuštěný příkaz operačního systému bude zrušen (!)
  5. V jednom skriptu lze použít více systémových volání. V takovém případě jsou spouštěny asynchronně (to znamená, že druhé nečeká na dokončení prvního)
  6. Paměť, kterou spotřebuje příkaz operačního systému se v takovém případě zahrnuje do maximálního množství paměti, kterou lze přidělit PHP skriptu. Neboli, pokud by běh externího procesu, spuštěného pomocí prováděcího operátoru potřeboval ke své činnosti více paměti, než kolik může PHP použít, nebude spuštěn.
  7. Jestliže běží PHP v safe-mode, prováděcí operátor nefunguje.

S prováděcím operátorem se tedy dá především na hodně místech narazit a tak je jeho použití spíš nedoporučované. Ale má-li webová stránka nějakým způsobem ovlivňovat hostitelský počítač, je prostor k jeho použití. Ekvivalentem prováděcího operátoru je PHP funkce shell_exec, což se jistě bude hodit zejména těm, kdo nechtějí lovit zpětné apostrofy na klávesnici.

Funkce system

Smyslem funkce system() je rozšířit shell_exec o možnost zpracovávat návratovou hodnotu volaného systémového příkazu. Jako nepovinný druhý parametr totiž může přebírat název proměnné, do níž se návratová hodnota shellového příkazu uloží. Takže můžete dělat takové věci jako testování návratové hodnoty a rozhodnutí se, co dělat. Funkce system však neumožňuje zpracovat celý výstup příkazu, protože v případě úspěchu vrací pouze poslední řádek výstupu. (V případě neúspěchu pak vrací FALSE).

Pro ty, kdo se s návratovými hodnotami ještě nesetkali - jsou to hodnoty vrácené provedeným programem, indikují převážně to, zda se programu "povedlo co zamýšlel" a bývají dokumentovány. Tak například GNU verze systémového příkazu grep může vrátit jako návratovou hodnotu nulu v případě, kdy byly nalezeny nějaké shody, jedničku pokud nebyly a dvojku pokud došlo k chybě. PHP funkce system() tedy může návratovou hodnotu zachytit a zpracovat.

Funkce exec

Funkce exec() je mocnější než funkce system v tom, že umožňuje jak získat návratovou hodnotu volaného systémového příkazu, tak i celý jeho výstup (ne tedy jen poslední řádek výstupu). Jde na to tak, že ukládá každý řádek výstupu do jednoho prvku pole. Konkrétní využití by mohlo spočívat třeba ve zpracování souborů protokolů, protože ty velmi často píší jednu zprávu na jeden řádek.

Funkce passthru

Funkce passthru() se prakticky ve všem podobá funkci system(), ale umí bezpečně zpracovat binární data. Jinak název vám může připomenout funkci fpassthru, která však vrací data ze souboru. Passthru samozřejmě vrací data výstupu příkazu operačního systému.

Bezpečnost

Spouštět ze skriptů příkazy pro operační systém hostitele je již z principu nebezpečná věc. Takže, jak již bylo uvedeno, většina správců to při konfiguraci PHP nadšeně zakáže. Nicméně, pokud byste chtěli a mohli výše uvedené příkazy použít, měli byste si si ověřit, že to, co předáváte jako příkaz operačnímu systému pokud možno nesouvisí s uživatelským vstupem. Jinak by totiž uživatel mohl zadat příkaz, který jste nezamýšleli.

Jestliže však přece jen musí být systémový příkaz závislý na zadání uživatele (čemuž byste se opravdu měli vyhýbat), vzniká problém. Podobný problém jsme již řešili při úvaze o uživatelských vstupech z formulářů. Špatná data z formuláře však mohla nejspíš poškodit zobrazenou stránku, nebo data z databáze. Tohle je mnohem horší, neošetřeným vstupem byste mohli dostat do potíží celý operační systém.

Obecně lze doporučit funkci escapeshellcmd. Ta oescapuje metaznaky shellu tak, aby byl předaný příkaz nezneužitelný. Trochu se svou filozofií podobá tomu, co v případě řetězců předávaných databázi dělá funkce addslashes, totiž že upravuje informace, které PHP předává nějakému externímu softwaru. Pakliže budete předávat nějaké parametry, často můžete trochu ovlivnit to, co obsahují. Třeba zda se jedná o číslo či řetězec, jak to bude velké či dlouhé nebo které znaky se tam smí a nesmí vyskytovat.

Rovněž byste neměli přiliš spoléhat na tvar, v jakém dorazí data z příkazu operačního systému. To se týká zejména situací, kdy se programátor spoléhá na výstupní data systémového příkazu. Pokud lze přepsat příkaz tak, aby mu stačila "jen" návratová hodnota externího procesu, udělejte to. Pokud ne, vrácená data analyzujte, než s nimi něco uděláte. Rovněž si uvědomte, že příkazy, které voláte, mohou po změně konfigurace systému vracet jiné výsledky či jinak formátovaná data.

Konečně, tohle je přesně ta situace, kdy je třeba probrat všechny katastrofické scénáře. Systémový příkaz může selhat, spotřebovat dostupnou paměť, čekat na uživatelský vstup, dlouho trvat, kolidovat s jiným vláknem či procesem, přestat odpovídat a já nevím, co ještě. Měli byste se ujistit, že s takovými situacemi počítáte a umíte je řešit.

V dalším díle seriálu se podíváme na to, jak použít PHP z příkazové řádky.

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=644