P°edßvßnφ parametr∙ pomocφ rozhranφ CGI

Ji°φ Kosek ml.

CGI-skripty, kterΘ jsme si ukßzali v poslednφm dφle serißlu, byly velmi jednoduchΘ. ZejmΘna proto, ₧e ke svΘ Φinnosti nepot°ebovaly ₧ßdnΘ informace od u₧ivatele. To vÜak nenφ p°φliÜ typick² p°φklad. Obvykle slou₧φ CGI-skripty jako rozhranφ pro prßci s r∙zn²mi databßzemi. U₧ivatel m∙₧e nap°φklad zadat klφΦovß slova, kterß ho zajφmajφ. KlφΦovß slova se p°edajφ CGI-skriptu a ten jako sv∙j v²sledek m∙₧e vygenerovat seznam vÜech Φlßnk∙ z databßze, kterΘ obsahujφ zadanß klφΦovß slova.

Z minula ji₧ vφme, ₧e parametry nejprve p°edß prohlφ₧eΦ serveru a ten je pak pomocφ rozhranφ CGI p°edß skriptu. Nejprve se tedy podφvßme na to, jak m∙₧e n∞jakß data poslat prohlφ₧eΦ serveru.

Existujφ dv∞ metody, jak p°enos dat uskuteΦnit. Prvnφ se jmenuje GET a slou₧φ pro p°enos kratÜφch informacφ. Pro p°enos v∞tÜφho mno₧stvφ dat pak slou₧φ metoda POST.

P°i pou₧itφ metody GET se vÜechny p°edßvanΘ informace p°ipojφ jako dotaz za otaznφk na konec URL, kterΘ ukazuje na CGI-skript. V dotazu je pot°eba provΘst drobnΘ ·pravy: vÜechny mezery se nahradφ znakem '+' a znaky se specißlnφm v²znamem se nahradφ sekvencφ znak∙ '%xx', kde xx je hexadecimßlnφ k≤d znaku. Nap°φklad lomφtko se p°evede na sekvenci '%2F'.

Pokud tedy chceme skriptu pokus.cgi jako parametr p°edat jmΘno 'Jan Novak', m∙₧eme pou₧φt jedno z nßsledujφcφch URL:

http://server/cgi-bin/pokus.cgi?Jan+Novak
http://server/cgi-bin/pokus.cgi?Jan%20Novak
V obou dvou p°φpadech jsme museli p°ek≤dovat mezeru tak, aby URL tvo°ilo jeden dlouh² nep°eruÜen² °et∞zec.

TakovΘto zadßvßnφ parametr∙ vÜak nenφ pro u₧ivatele zrovna pohodlnΘ. NaÜt∞stφ existuje n∞kolik u₧ivatelsky p°φjemn∞jÜφch zp∙sob∙, jak p°im∞t prohlφ₧eΦ k vygenerovßnφ pot°ebnΘho URL s parametry:

Pokud chceme pro p°edßnφ dat pou₧φt metodu POST, musφme na strßnce op∞t pou₧φt formulß°, ale jako metodu uvΘst POST. Data se k≤dujφ stejn²m zp∙sobem jako v metod∞ GET, ale pro jejich odeslßnφ se vytvo°φ zvlßÜtnφ datovΘ spojenφ mezi prohlφ₧eΦem a serverem. To umo₧≥uje p°enßÜet v∞tÜφ objemy dat ne₧ p°idßnφ parametr∙ na konec URL.

Zp∙sob zak≤dovßnφ ·daj∙ z formulß°e si p°edvedeme na malΘ ukßzce. P°edpoklßdejme nßÜ znßm² formulß° z p°edchozφch dφl∙:

<FORM ACTION="http://server/cgi-bin/obsluha.cgi" METHOD=GET>
JmΘno: <INPUT TYPE=TEXT NAME=jmeno><BR>
V∞k: <INPUT TYPE=TEXT NAME=vek><BR>
<INPUT TYPE=SUBMIT VALUE="Odeslßnφ formulß°e">
</FORM>
Pokud u₧ivatel vyplnφ jako jmΘno 'Pavel Severa' a jako v∞k '47', prohlφ₧eΦ po stisknutφ tlaΦφtka Odeslßnφ formulß°e vygeneruje nßsledujφcφ po₧adavek:
http://server/cgi-bin/obsluha.cgi?jmeno=Pavel+Severa&vek=47
Vidφme, ₧e obsah jednotliv²ch polφ je identifikovßn sv²m jmΘnem a pole jsou odd∞lena znakem '&'.

U₧ vφme, jak m∙₧e prohlφ₧eΦ poslat data na server. Vra¥me se tedy k otßzce, jak server p°edß data naÜemu skriptu. Rozhranφ CGI nßm nabφzφ t°i mo₧nosti:

  1. Data jsou skriptu p°edßna jako parametry na p°φkazovΘ °ßdce. To p°ichßzφ v ·vahu p°i p°edßvßnφ opravdu krßtkΘ informace pomocφ metody GET (klφΦovß slova zadanß pomocφ <ISINDEX> nebo sou°adnice na klikacφ map∞).
  2. Data jsou p°edßna v prom∞nnΘ prost°edφ QUERY_STRING. Tento zp∙sob je typick² pro p°edßvßnφ dat z formulß°e odeslanΘho metodou GET.
  3. Data jsou p°edßna na standardnφ vstup skriptu -- zp∙sob typick² pro p°edßnφ dat z formulß°e odeslanΘho metodou POST.
Krom∞ dat od u₧ivatele, kterß p°edß server skriptu, mßme k dispozici dalÜφ u₧iteΦnΘ informace ulo₧enΘ v prom∞nn²ch prost°edφ (viz tab. 1).

Tab. 1: Prom∞nnΘ prost°edφ p°edßvanΘ skriptu rozhranφm CGI
Prom∞nnßObsah
REQUEST_METHOD urΦuje zp∙sob p°edßvßnφ informacφ -- GET nebo POST
QUERY_STRING obsahuje data p°enßÜenß metodou GET
PATH_INFO cesta, kterß mß b²t zpracovßna skriptem; nejΦast∞ji jde o Φßst cesty v URL za jmΘnem skriptu
PATH_TRANSLATED cesta ke stejnΘmu souboru jako PATH_INFO; v tomto p°φpad∞ vÜak byla cesta p°emapovßna podle konfigurace serveru
CONTENT_TYPE MIME typ dat zasφlan²ch metodou POST
CONTENT_LENGTH dΘlka dat zasφlan²ch metodou POST
SCRIPT_NAME URL prßv∞ provßd∞nΘho skriptu
SERVER_NAME jmΘno serveru
SERVER_PORT Φφslo portu
SERVER_SOFTWARE jmΘno a verze programu pracujφcφho jako WWW-server
SERVER_PROTOCOL jmΘno a verze protokolu, kter²m p°iÜel po₧adavek (typicky HTTP/1.0 nebo HTTP/1.1)
GATEWAY_INTERFACE oznaΦenφ a verze pou₧itΘho rozhranφ ke spuÜt∞nφ skriptu (typicky CGI/1.1)
REMOTE_HOST domΘnovß adresa poΦφtaΦe, z n∞j₧ p°iÜel po₧adavek
REMOTE_ADDR IP-adresa poΦφtaΦe, z n∞j₧ p°iÜel po₧adavek
AUTH_TYPE zp∙sob pou₧itΘ autorizace u₧ivatele
REMOTE_USER v p°φpad∞, ₧e byl u₧ivatel autorizovßn, obsahuje tato prom∞nnß jeho jmΘno
REMOTE_IDENT informace o identit∞ zφskanß zp∞tn²m dotazem u klienta; tuto vlastnost p°φliÜ mnoho server∙ nevyu₧φvß

Nynφ u₧ toho vφme dost na to, abychom si ukßzali CGI-skript, kter² bude zpracovßvat parametry zadanΘ u₧ivatelem. Pou₧ijeme nßÜ osv∞dΦen² p°φklad, kter² hodnotφ u₧ivatele podle jeho v∞ku. Zadßnφ ·daj∙ umo₧nφme u₧ivateli v²Üe uveden²m formulß°em. Skript, kter² vyhodnotφ ·daje na formulß°i, pojmenujeme obsluha.cgi.

#!/bin/sh
echo 'Content-type: text/html'
echo
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">'
echo '<HTML>'
echo '<HEAD>'
echo '<TITLE>Obsluha formulß°e</TITLE>'
echo '</HEAD>'
echo '<BODY>'
echo '<H1>V²sledek obsluhy formulß°e</H1>'
eval `echo $QUERY_STRING | awk 'BEGIN{RS="&"} {printf "WWW_%s\n",$1}' `
WWW_jmeno=`echo $WWW_jmeno | tr "+" "\040"`
echo "$WWW_jmeno je"
if [ $WWW_vek -lt 10 ]; then
   echo 'p∞knej mlφΦnßk'
elif [ $WWW_vek -lt 20 ]; then
   echo 'teenager'
elif [ $WWW_vek -lt 60 ]; then
   echo 'v nejlepÜφch letech'
elif [ $WWW_vek -lt 100 ]; then
   echo 'je pravd∞podobn∞ prarodiΦ'
else
   echo 'je n∞kde mezi stovkou a smrtφ'
fi
echo '</BODY>'
echo '</HTML>'
CGI-skript jsme op∞t zapsali v p°φkazovΘm interpretu sh. P°edem upozor≥uji, ₧e po vyzkouÜenφ skriptu, je nejlepÜφ jej ihned smazat. NßÜ skript toti₧ nenφ zdaleka bezpeΦn², jak si za chvφli ukß₧eme.

Podφvejme se nynφ na skript troÜku podrobn∞ji. Prvnφ °ßdek obsahuje urΦenφ interpretu, kter² se na skript pou₧ije. Dßle pak generujeme HTTP hlaviΦku a kostru HTML strßnky.

Dva °ßdky na zaΦßtku t∞la strßnky jsou opravdu magickΘ. Prvnφ z nich pro ka₧dΘ pole formulß°e, jeho₧ hodnota je p°edßna v prom∞nnΘ QUERY_STRING, vytvo°φ prom∞nnou WWW_jmΘno-pole s obsahem p°φsluÜnΘho pole. To nßm usnadnφ dalÜφ prßci s p°edan²mi parametry. Druh² °ßdek v prom∞nnΘ WWW_jmeno (ta nese obsah vstupnφho pole jmeno) nahradφ vÜechny v²skyty znaku '+' mezerou (ASCII k≤d 40 v osmiΦkovΘ soustav∞).

Podmφnka [ $WWW_vek -lt n ] je spln∞na pokud je prom∞nnß $WWW_vek menÜφ ne₧ n. N∞kolik vno°en²ch podmφnek zajistφ vytiÜt∞nφ p°φsluÜnΘho komentß°e podle v∞ku u₧ivatele.

Vidφme, ₧e tvorba skript∙ cestou p°φkazovΘho interpretu je urΦena spφÜe pro UnixovΘho guru, ne₧li pro "normßlnφho Φlov∞ka".

BezpeΦnost a CGI-skripty

Pro psanφ CGI-skript∙ se nejΦast∞ji pou₧φvajφ interpretovanΘ jazyky jako Bourne shell (sh) nebo Perl. To vÜak p°inßÜφ velkß bezpeΦnostnφ rizika. Ukß₧eme si zde bezpeΦnostnφ dφru v naÜem skriptu.

Pokud n∞kdo bude chtφt nßÜ skript zneu₧φt pro zjiÜt∞nφ d∙le₧it²ch informacφ o systΘmu, m∙₧e vyu₧φt n∞kterΘ mΘn∞ znßmΘ vlastnosti interpretu sh. Mezi n∞ pat°φ i to, ₧e na jednΘ °ßdce lze p°φkazy odd∞lovat pomocφ st°ednφku. Pokud tedy n∞jak² "chytrßk" zavolß nßÜ skript pomocφ nßsledujφcφho URL:

http://server/cgi-bin/obsluha.cgi?;who
Dojde v CGI-skriptu k expanzi p°φkazu echo $QUERY_STRING na echo ;who. V²sledkem je, ₧e na v²stup skriptu se zapφÜe prßzdnß °ßdka (p°φkaz echo) a v²sledek programu who. Zcela kdokoliv tak m∙₧e zjistit, kdo v danou chvφli pracuje na serveru. Mφsto p°φkazu who m∙₧eme pou₧φt libovoln² jin² p°φkaz. Pokud pou₧ijeme nap°. URL:
http://server/cgi-bin/obsluha.cgi?;cat</etc/passwd
Vrßtφ se nßm strßnka, kterß na svΘm zaΦßtku bude obsahovat v²pis informacφ o vÜech u₧ivatelφch systΘmu. Z tohoto v²pisu lze zjistit u₧ivatele, kte°φ heslo nemajφ nastaveno, a u t∞ch ostatnφch lze pom∞rn∞ snadno jejich heslo rozÜifrovat. Kdokoliv se pak pomocφ telnetu m∙₧e p°ipojit na server a provßd∞t tam tΘm∞° cokoliv.

Pokud tedy chceme vytvß°et bezpeΦnΘ skripty v interpretovan²ch jazycφch, musφme v₧dy p°φchozφ data zkontrolovat a p°ed jejich zpracovßnφm z nich eliminovat nebezpeΦnΘ znaky jako je st°ednφk.

OÜet°it vÜechny nebezpeΦnΘ kombinace vstupnφch parametr∙ nenφ vÜak v∙bec jednoduchΘ a je celkem pravd∞podobnΘ, ₧e na n∞jakou mo₧nost zapomeneme. Je proto lepÜφ CGI-skripty psßt v jazycφch, ve kter²ch je nutno program p°ed spuÜt∞nφm p°elo₧it do spustitelnΘ formy. V t∞chto jazycφch (C, C++, Java apod.) je pak v²Üe zmφn∞nΘ nebezpeΦφ obelst∞nφ skriptu do znaΦnΘ mφry eliminovßno.


Vid∞li jsme, ₧e CGI-skripty jsou jako ohe≥ -- dobr² sluha, ale Üpatn² pßn. V serißlu se budeme pro p°φÜt∞ zab²vat p°edevÜφm skriptovacφmi jazyky, kterΘ se vklßdajφ p°φmo do HTML strßnky, jako je PHP a ASP. Prßce s nimi je jednoduÜÜφ a v∞tÜinou i efektivn∞jÜφ. Pro specißlnφ aplikace vÜak m∙₧e b²t v²hodnΘ pou₧φt CGI-skripty -- nynφ ji₧ znßte princip jejich prßce a zp∙sob p°edßvßnφ parametr∙.

V p°φÜtφch pokraΦovßnφch serißlu se podφvßme na r∙znΘ druhy vstupnφch polφ, kterΘ lze pou₧φvat ve webovsk²ch formulß°φch.

© Ji°φ Kosek 1999