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 34701×
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.
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:
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.
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()
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()
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.
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.