<p id='prepend'>Vytvori¥ bezpeΦn· autorizßciu, Φi₧e prφstup na strßnky pod u₧φvate╛sk²m menom a heslom, je ne╛ahkß ·loha. Ako oÜetri¥ neoprßvnenΘ prihlßsenie pomocou PHP so sessions v kombinßcii s databßzou MySQL si preΦφtate v tomto Φlßnku.</p>
<p>Ako teoretick² ·vod posl·₧i Φlßnok <a href='http://interval.cz/clanek.asp?article=1408' title='R∙₧iΦka, Pavel: BezpeΦnost p°edevÜφm û cross-site skripting a session-stealing'>BezpeΦnost p°edevÜφm - cross-site skripting a session-stealing</a>, ktor² varuje pred mo₧n²mi chybami pri pφsanφ takΘhoto skriptu. Vychßdzajme z obvyklej situßcie, kde mßme u₧φvate╛ov systΘmu zapφsan²ch napr. v tabu╛ke <strong>uzivatelia</strong>, ktorß obsahuje polo₧ky <strong>uid</strong> (jedineΦnΘ Φφslo u₧φvate╛a), <strong>meno</strong> (prihlasovacie meno) a polo₧ku <strong>heslo</strong>, kde budeme uchovßva¥ heslo u₧φvate╛a samozrejme zahashovanΘ - v naÜom prφpade algoritmom md5. SQL prφkaz pre vytvorenie tabu╛ky <strong>uzivatelia</strong> bude nasledovn²:</p>
<div class='sample'>
CREATE TABLE uzivatelia (
<br /> uid int(10) unsigned NOT NULL auto_increment,
<br /> meno varchar(12) NOT NULL,
<br /> heslo varchar(32) NOT NULL,
<br /> UNIQUE uid (uid)
<br />);
</div>
<p>Pre autorizaΦn² skript budeme potrebova¥ eÜte jednu tabu╛ku <strong>autorizacia</strong>, ktorß nßm posl·₧i na evidenciu prihlßsen²ch u₧φvate╛ov, konkrΘtne polo₧ky <strong>sess</strong> (session id), <strong>cas</strong> (Φas prihlßsenia, resp. Φas poslednΘho prφstupu na server) a polo₧ka <strong>user_info</strong> do ktorej si zapφÜeme ·daje o IP adrese u₧φvate╛a pri prihlßsenφ a poΦas celej doby prihlßsenia budeme hodnotu tejto polo₧ky kontrolova¥ s aktußlnym stavom. OpΣ¥ uvßdzam SQL skript pre vytvorenie tabu╛ky <strong>autorizacia</strong>:</p>
<div class='sample'>
CREATE TABLE autorizacia (
<br /> aid int(10) unsigned NOT NULL auto_increment,
<br /> sess varchar(32) NOT NULL,
<br /> cas int(10) unsigned DEFAULT '0' NOT NULL,
<br /> user_info varchar(32) NOT NULL,
<br /> UNIQUE aid (aid)
<br />);
</div>
<p>Za jednou IP adresou sa m⌠₧e nachßdza¥ nieko╛ko r⌠znych poΦφtaΦov, preto je potrebnΘ skutoΦn· IP adresu zis¥ova¥ sp⌠sobom popφsan²m v hore citovanom Φlßnku:</p>
<div class='sample'>
$IPadresa=$REMOTE_ADDR;
<br />$IPadresa.="@".$HTTP_X_FORWARDED_FOR;
<br />$IPadresa.="@".$HTTP_FORWARDED;
<br />$IPadresa.="@".$HTTP_CLIENT_IP;
<br />$IPadresa.="@".$X_HTTP_FORWARDED_FOR;
</div>
<p>╚o, ₧ia╛, taktie₧ nerieÜi jedineΦn· identifikßciu poΦφtaΦa, vo vΣΦÜine prφpadoch bude nastavenß len premennß $REMOTE_ADDR, ostatnΘ premennΘ m⌠₧u, ale aj nemusia by¥ nastavenΘ. Proxy posielaj· tieto HTTP hlaviΦky, ale nastavenφm je mo₧nΘ ich zakßza¥. Ja vyu₧φvam na identifikßciu IP adresy funkciu <strong>getVisitorIdentifier</strong>, ktor· nßjdete na strßnke <a href='http://www.cgsa.net/php/identifierShow.php'>identifier PHP functions by Marc Meurrens</a>, Φo je v podstate upravenß vyÜÜie uvedenß identifikßcia s rozlφÜenφm r⌠znych prφpadov nastavenφ (viac napovedia komentßre vo funkcii).</p>
<p>Kontrola IP adresy poΦas doby prihlßsenia m⌠₧e sp⌠sobova¥ problΘmy dial-up u₧φvate╛om, u ktor²ch <strong>sa IP adresa menφ dynamicky pri ka₧dom pripojenφ</strong>. Netreba sa vÜak obßva¥ toho, ₧e prφdeme o dial-up u₧φvate╛ov, treba ich vÜak na t·to situßciu pri prihlasovanφ upozorni¥.</p>
<p>JedineΦnß identifikßcia u₧φvate╛a (resp. poΦφtaΦa) nie je mo₧nß, avÜak k funkcii <strong>getVisitorIdentifier</strong> m⌠₧eme doplni¥ ako dodatoΦn· informßciu typ prehliadaΦa, Φφm zv²Üime presnos¥ identifikßcie. Preddefinovanß premennß <strong>$_SERVER['HTTP_USER_AGENT']</strong> obsahuje re¥azec identifikuj·ci prehliadaΦ, napr. <samp>Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.1) Opera 5.12 [en]</samp>.</p>
<p>Na ukß₧ku fungovania autorizßcie si vytvorφme nasledovnΘ s·bory:</p>
<div class='list'>
<ul>
<li><strong>index.php</strong> - hlavnß strßnka (cez t·to strßnku vkladßme obsah ∩a╛Üφch strßnok, cez require)</li>
<li><strong>login.php</strong> - formulßr pre prihlßsenie</li>
<li><strong>autorizacia.php</strong> - funkcia pre autorizßciu</li>
<li><strong>dalsia1.php, dalsia2.php, dalsia3.php</strong> - s·bory, ktorΘ sa bud· vklada¥ ako obsah strßnky pod╛a zvolenΘho menu</li>
</ul>
</div>
<p>S·bor <strong>index.php</strong> obsahuje funkciu <strong>Autorizacia</strong>, ktorß pri ·speÜnom prihlßsenφ vrßti true, inak false a vlo₧φ formulßr pre prihlßsenie u₧φvate╛a. Na zßklade zvolenΘho menu vkladßme obsah strßnok dalsia1.php, dalsia2.php, dalsia3.php. V²stup strßnky posielame do bufferu, preto₧e ∩alej v skripte volßme funkciu <strong>SetCookie</strong>, ktor· je potrebnΘ vola¥ eÜte pred nejak²m v²stupom. Viac o posielanφ v²stupu do bufferu sa m⌠₧ete doΦφta¥ v Φlßnku <a href='http://interval.cz/clanek.asp?article=1063' title='Kocman, Ji°φ: UÜet°ete a₧ 80 % datovΘho p°enosu'>UÜet°ete a₧ 80 % datovΘho p°enosu</a>.</p>
<div class='sample'>
<?php
<br />ob_start ();
<br />session_start ();
<br />
<br />require "settings.php";
<br />require "autorizacia.php";
<br />require "head.php";
<br />
<br />if (Autorizacia () == false) {
<br /> require "login.php";
<br /> require "foot.php";
<br /> exit ();
<br /> }
<br />
<br />switch ($_GET["menu"]){
<br /> case "dalsia2":
<br /> case "dalsia3":
<br /> require $_GET["menu"].".php";
<br /> break;
<br /> default:
<br /> require "dalsia1.php";
<br /> break;
<br /> }
<br />
<br />require "foot.php";
<br />ob_end_flush ();
<br />?>
</div>
<p>S·bor <strong>head.php</strong> obsahuje pripojenie k databßze a menu s odkazmi na ∩alÜie strßnky <samp><a href="index.php?menu=dalsia1<strong><?php echo (SID?"&".SID:"");?></strong>">Dalsia 1</a></samp>, teda ak je to potrebnΘ, doplnφme session id do odkazov (session.use_trans_sid = 0). Ak je to mo₧nΘ, pou₧ite perzistentnΘ spojenie.</p>
<p>S·bor <strong>foot.php</strong> uzatvßra HTML obsah strßnky a spojenie s databßzou. S·bor <strong>settins.php</strong> obsahuje nastavenia k autorizßcii, v²znam je zrejm² z komentßrov.</p>
<div class='sample'>
<?php
<br /><span class='comment'>// debug mode</span>
<br />define ("MK_DEBUG", true);
<br />
<br /><span class='comment'>// typ prihlasenia (0=select, 1=input)</span>
<br />define ("AUTH_TYPE", 1);
<br />
<br /><span class='comment'>// timeout autoodhlasenia (v sekundach)</span>
<br />define ("AUTH_TIMEOUT", 60 * 60);
<br />
<br /><span class='comment'>// ci bude spravcovske heslo platit pre vsetkych</span>
<br />define ("AUTH_USEADMINPASSWD", false);
<br />
<br /><span class='comment'>// ak bude prazdna tabulka uzivatelia, automaticky sa vytvori user AUTH_ADMINUSERNAME, ktory bude povazovany za spravcu (uid=1)</span>
<br />define ("AUTH_ADMINUSERNAME", "[sprßvca]");
<br />
<br /><span class='comment'>// heslo pre automaticky vytvoreneho spravcu (md5)</span>
<p>S·bor <strong>login.php</strong> obsahuje formulßr pre prihlßsenie u₧φvate╛a. Na zßklade konÜtanty <strong>AUTH_TYPE</strong> generujeme HTML formulßr s input alebo option prvkami pre zadanie u₧φvate╛skΘho mena. Ak tabu╛ka <strong>uzivatelia</strong> neobsahuje ₧iadny zßznam, bude vytvoren² u₧φvate╛ <strong>AUTH_ADMINUSERNAME</strong> s heslom <strong>AUTH_ADMINPASSWD</strong>. Premennß <strong>$username</strong>, zφskanß cez <samp>$username = <strong>$_POST</strong>["username"] ? <strong>$_POST</strong>["username"] : <strong>$_COOKIE</strong>["username"];</samp>, bude obsahova¥ hodnotu naposledy prihlßsenΘho u₧φvate╛a (zistenΘ z cookies), alebo hodnotu aktußlne prihlasovanΘho u₧φvate╛a (napr. pri nesprßvne zadanom hesle). V input prvku sa dosadφ username ako hodnota, pri option prvku bude tßto vo╛ba s parametrom selected. JavaScriptom nastavφme kurzor do potrebnΘho prvku.</p>
<p>S·bor <strong>autorizacia.php</strong> obsahuje funkcie pre spracovanie prihlasovacieho formulßra a nßsledne kontrolu session id a zahashovan· hodnotu IP adresy s typom browsera. V ·vode funkcie kontrolujeme, Φi nebolo zvolenΘ odhlßsenie danΘho u₧φvate╛a, ak ßno, funkcia Logout sa postarß o vymazanie zßznamu s t²mto session id z tabu╛ky autorizacia a odstrßni session premennΘ (<strong>session_destroy</strong> by sp⌠sobilo vymazanie session a <strong>session_id</strong> by bolo prßzdne, preto si toto session id ponehßme aj pre ∩alÜie prihlßsenie, niΦ t²m nepokazφme, len odstrßnime jeho premennΘ).</p>
<div class='sample'>
function Autorizacia () {
<br /> if ($_GET["menu"] == "logout"){
<br /> Logout ();
<br /> return false;
<br /> }
<br /> ...
<br /> ...
<br />
<br />function Logout () {
<br /> <span class='comment'>// zmaz zaznam so session_id</span>
<br /> $result = mysql_query ("DELETE FROM autorizacia WHERE sess='".session_id ()."'") or die();
<p>V ∩alÜom kroku vyhodnotφme podmienku <samp>isset($_POST["username"]) && isset($_POST["passwd"]) && empty($_SESSION["uid"])</samp>, ktorß nßm pri splnenφ hovorφ o tom, ₧e boli vyplnenΘ vstupnΘ hodnoty a u₧φvate╛ sa prßve prihlasuje (eÜte nie je prihlßsen²), Φo oÜetruje situßciu, ak by bol ihne∩ po prihlßsenφ vyvolan² refresh strßnky. ZadanΘ heslo zahashujeme funkciou md5 <samp>$_POST["passwd"] = md5 ($_POST["passwd"]);</samp>. Ak bude nastavenß konÜtanta <strong>AUTH_USEADMINPASSWD</strong>, sprßvcovskΘ heslo (zßznam s uid=1) bude platnΘ pre vÜek²ch ostatn²ch u₧φvate╛ov. Zφskanie sprßvcovskΘho hesla:</p>
<br /> $result = mysql_query ("SELECT heslo FROM uzivatelia WHERE uid='1'") or die();
<br /> $row = mysql_fetch_object($result);
<br /> $admin_passwd = $row->heslo;
<br /> }
</div>
<p>Teraz sa pok·sime z tabu╛ky uzivatelia vybra¥ zßznam so zadan²m menom a heslom. OpΣ¥ bereme do ·vahy aj nastavenie konÜtanty <strong>AUTH_USEADMINPASSWD</strong>:</p>
<div class='sample'>
<span class='comment'>// vyber uzivatela "username" s heslom "passwd"</span>
<br />$result = mysql_query ("SELECT * FROM uzivatelia WHERE meno='".$_POST["username"]."' AND heslo='".$_POST["passwd"]."'") or die();
<br /> <span class='comment'>// heslo je OK - vyber uzivatela "username"</span>
<br /> $result = mysql_query ("SELECT * FROM uzivatelia WHERE meno='".$_POST["username"]."'") or die();
<br /> return AuthInsert ($result);
<br /> }
<br /> }
<br />return AuthInsert ($result);
</div>
<p>Funkcia <strong>AuthInsert</strong> sa nßm pri nßjdenφ tohoto zßznamu postarß o pridanie u₧φvate╛a do tabu╛ky autorizacia, kde si pri vlo₧enφ zßznamu zapφÜeme session id, Φas prihlßsenia a zahashovanΘ informßcie o IP adrese a type prehliadaΦa - funkcia <strong>GetUserInfo</strong>. Funkciu function <strong>getVisitorIdentifier</strong> som pridal z u₧ spomφnanej strßnky Marca Meurrensa. Z tabu╛ky autorizacia eÜte vyma₧eme zßznamy, ktor²m vyprÜal Φas AUTH_TIMEOUT od poslednej akcie so serverom. Cez <strong>SetCookie</strong> nastavφme premenn· <strong>$username</strong> na hodnotu prßve prihlßsenΘho u₧φvate╛a, ktor· vyu₧φvame v s·bore <strong>login.php</strong>. Do superglobßlnej premennej $_SESSION vlo₧φme hodnoty, ktorΘ m⌠₧eme naΦφta¥ napr. z tabu╛ky u₧φvatelia a vyu₧φva¥ ich poΦas doby prihlßsenia. Povinnß je hodnota <strong>uid</strong>, ktor· vyu₧φvame v spomφnanej podmienke funkcie Autorizacia <samp>isset($_POST["username"]) && isset($_POST["passwd"]) && empty($_SESSION["uid"])</samp>. Pri ·speÜnom pridanφ zßznamu do tabu╛ky autorizacia vrßti funkcia AuthInsert hodnotu true, inak false - ne·speÜnΘ prihlßsenie. Funkcia <strong>HTMLlogout</strong> v dobe prihlßsenΘho u₧φvate╛a zobrazφ odkaz n odhlßsenie u₧φvate╛a (menu=logout).</p>
<div class='sample'>
function AuthInsert ($result) {
<br /> if (mysql_num_rows($result)) {
<br /> $row = mysql_fetch_object ($result);
<br />
<br /> <span class='comment'>// pridaj session_id a cas prihlasenia do tabulky autorizacia</span>
<br /> mysql_query ("INSERT INTO autorizacia (sess, cas, user_info) VALUES ('".session_id ()."', '".time()."', '".GetUserInfo()."')") or die();
<br /> <span class='comment'>// vymaz stare zaznamy</span>
<br /> mysql_query ("DELETE FROM autorizacia WHERE cas < ".(time() - AUTH_TIMEOUT)) or die();
<p>Ak je u₧φvate╛ prihlßsen², je potrebnΘ prekontrolova¥, Φi sa zßznam s dan²m session id a zahashovanou IP adresou s typom browsera nachßdza v zßznamoch tabu╛ky autorizacia. Pri existencii takΘhoto zßznamu prekontrolujeme, Φi nevyprÜal Φas AUTH_TIMEOUT a nastavφme nov· hodnotu Φasu prφstupu na server. Ak zßznam neexistuje, alebo vyprÜal Φas neΦinnosti poΦas doby prihlßsenia vrßti nßm funkcia Autorizacia hodnotu false (vyma₧· sa tie₧ ostatnΘ zßznamy s vyprÜan²m Φasom a odstrßni sa aktußlne session), inak vrßti true.</p>
<div class='sample'>
<span class='comment'>// nebol zadany uzivatel ani heslo</span>
<br /><span class='comment'>// existuje zaznam s takymto session_id ?</span>
<br />$result = mysql_query ("SELECT * FROM autorizacia WHERE sess='".session_id ()."' AND user_info='".GetUserInfo()."'") or die();
<br /><span class='comment'>// ano existuje</span>
<br /> mysql_query ("UPDATE autorizacia SET cas='".time()."' WHERE aid='".$row->aid."'") or die();
<br /> return HTMLlogout ();
<br /> }
<br /> <span class='comment'>// ano vyprsal</span>
<br /> else {
<br /> mysql_query ("DELETE FROM autorizacia WHERE cas < ".(time() - AUTH_TIMEOUT)) or die();
<br /> session_destroy ();
<br /> }
<br /> }
<br />return false;
</div>
<p>Najd⌠le₧itejÜφm bezpeΦnostn²m prvkom autorizßcie bude protokol <strong>https</strong>. Korektne napφsan² skript vyu₧φvaj·ci https musφ obsahova¥ vÜetky odkazy s absol·tnou cestou, k tomu si medzi konÜtanty (settings.php) vlo₧φme konÜtantu definuj·cu absol·tnu cestu <samp>define ("HTTPS_PATH", "https://localhost/autorizacia/");</samp> k s·borom, ktorΘ si ₧elßme ma¥ zabezpeΦenΘ. V downloade zdrojov²ch k≤dov nßjdete tieto odkazy u₧ upravenΘ.</p>
<p>Ukladanie sessions na serveroch obvykle ostßva v prednastavenom adresßri <strong>/tmp</strong>, avÜak k zv²Üeniu bezpeΦnosti je vhodnΘ si session uklada¥ do nami zvolenΘho adresßra, a to jednoduch²m pridanφm funkcie <samp>session_save_path("session/autorizacia/")</samp> eÜte pred volanφm session_start().</p>
<p>Okrem s·borov m⌠₧eme session uklada¥ aj do databßzy, Φo pri sprßvnom nastavenφ prßv k databßze posunie ·rove≥ bezpeΦnosti zas o nieΦo vyÜÜie. Na ukladanie sessions do databßzy je potrebnß funkcia <strong>session_set_save_handler</strong> plus ∩alÜie nami definovanΘ funkcie k prßci so sessions. Tieto funkcie m⌠₧eme nßjs¥ na internete, napr. na strßnke <a href='>http://www.phpbuilder.com/columns/ying20000602.php3?page=1'>Custom Session Handlers in PHP4</a>. Implementßcia pozostßva z vytvorenia tabu╛ky <strong>sessions</strong>, ktorej Ütrukt·ru nßjdete v s·bore <strong>session_mysql.php</strong>, a vlo₧enia tohto s·boru pred session_start v index.php.</p>
<p>Pre ukladanie sessions do databßzy je potrebnΘ nastavi¥ direktφvu <strong>session.save_handler = user</strong> v <strong>php.ini</strong>, alebo priamo v naÜich skriptoch funkciou <strong>ini_set("session.save_handler","user")</strong>.</p>
<p>Aby sme zabrßnili vkladaniu generovan²ch hesiel, poistφme strßnku obrßzkom s re¥azcom, ktor² bude potrebnΘ zapφsa¥ pri prihlasovanφ. Obohatφme formulßr v s·bore login.php nasledovn²m k≤dom:</p>
<div class='sample'>
Kontroln² re¥azec :
<br /><span class='comment'><!-- ROZMERY IMG TREBA UPRAVIT PRI ZMENE KONSTANT V ImgRndStr.php --></span>
<p>S·bor <strong>ImgRndStr.php</strong> vytvßra obrßzok s nßhodn²m re¥azcom a taktie₧ tento re¥azec zapφÜe do session. Po zadanφ mena a hesla skontrolujeme, Φi je zadan² a session re¥azec zhodn². Doplnok do funkcie Autorizacia (tuΦnΘ pφsmo):</p>
<div class='sample'>
function Autorizacia () {
<br /> if ($_GET["menu"] == "logout"){
<br /> Logout ();
<br /> return false;
<br /> }
<br />
<br /> if (isset($_POST["username"]) && isset($_POST["passwd"]) && empty($_SESSION["uid"])) {
<p>Aby bolo aj pre poΦφtaΦ nßroΦnΘ rozpozna¥, ak² znak sa v obrßzku nachßdza, zvolil som pri generovanφ re¥azca met≤du nßhodnΘho vypos·vania znakov v horizontßlnom aj vertikßlnom smere, samozrejme v hraniciach rozpoznania Φlovekom. Pre zobrazenie znakov vyu₧φvam zßkladn² font 5 (mo₧nΘ pou₧i¥ 1 a₧ 5, priΦom vΣΦÜie Φφslo udßva vΣΦÜφ font), ktor² je s·Φas¥ou grafickej kni₧nice PHP (ak nie je zakompilovanß ako s·Φas¥ PHP treba v php.ini navoli¥: extension=php_gd(2).dll (.so pre linux), alebo v ·vode skriptu naΦφta¥ cez funkciu <strong>dl</strong>). Ke∩₧e sa jednß o neproporcionßlne pφsmo, v ·vode si zistφme v²Üku a Üφrku znaku. ╧alej vytvorφme obrßzok s rozmermi pod╛a poΦtu znakov a rozsahu horizontßlnej premennej, ktorß urΦuje ako ve╛mi bud· znaky äskßka¥ô v horizontßlnom smere. NiektorΘ konÜtanty (farby, font, dσ₧ku re¥azca, typ nßhodn²ch znakov a horizontßlnu premenn·) je mo₧nΘ jednoducho zmeni¥ na zaΦiatku skriptu ImgRndStr.php. Znak nula, ve╛kΘ a malΘ pφsmeno O sa v generovanom re¥azci nevyskytuje (nahradenΘ cez funkciu strtr), preto₧e s· navzßjom dos¥ podobnΘ. Nßhodn² posun vo vertikßlnom smere je v rozsahu jednΘho pixelu. Taktie₧ je mo₧nΘ ako podklad vlo₧i¥ obrßzok, ktor² by eÜte viac skomplikoval dek≤dovanie re¥azca z obrßzku.</p>
<p>Funkciu <strong>ImageColorAllocate</strong> som rozÜφril o zadßvanie farieb typu #rrggbb, ktorej nßzov je ImageColorAllocateHex.</p>
<p>A pre ·plnos¥ k≤d s·boru imgrndstr.php (pri ukladanφ sessions do databßzy vlo₧i¥ pred session_start riadok <samp>require "session_mysql.php";</samp> - ak preferujete t·to mo₧nos¥, je vhodnΘ upravi¥ skript tak, aby sme sa k databßze pripßjali len raz):</p>
<p>Pre maximßlnu bezpeΦnos¥ je mo₧nΘ naviac generova¥ dodatoΦnΘ heslo a to potom zasiela¥ u₧φvate╛om na mail, alebo formou SMS, ale pre be₧nΘ prihlasovanie myslφm tak²to sp⌠sob postaΦφ.</p>
<p><a href='podklady/kubis/653/autorizacia.zip'>S·bory, potrebnΘ k rozbehaniu autorizßcie</a>, s· vßm k dispozφcii.</p>
<h4><a href='http://interval.cz/redirect/redirect.asp?what=interval_booknews&url=http://interval.inshop.cz/inshop/scripts/detail.asp?ItemID=226'>Hacking bez tajemstvφ: Java a J2EE</a></h4>
<li><a href='http://interval.cz/redirect/redirect.asp?what=interval_offline&url=http://interval.cz/clanek.asp?article=2752'>Java a 3D grafika</a></li>
<li><a href='http://interval.cz/redirect/redirect.asp?what=interval_offline&url=http://interval.cz/clanek.asp?article=2694'>Czech.NET Forum - diskusnφ f≤rum v ASP.NET</a></li>
<li><a href='http://interval.cz/redirect/redirect.asp?what=interval_offline&url=http://interval.cz/clanek.asp?article=2767'>Zprßvy z Proffesional Developers Conference (PDC 2003)</a></li>
<li><a href='http://interval.cz/redirect/redirect.asp?what=interval_offline&url=http://interval.cz/clanek.asp?article=2702'>KonfiguraΦnφ soubor v PHP, tentokrßt ve formßtu .ini</a></li>
<li><a href='http://interval.cz/redirect/redirect.asp?what=interval_offline&url=http://interval.cz/clanek.asp?article=2561'>Internet Explorer 7 se jmenuje MyIE2</a><div>143 komentß°∙ </div></li>
<li><a href='http://interval.cz/redirect/redirect.asp?what=interval_offline&url=http://interval.cz/clanek.asp?article=1328'>XHTML v praxi</a><div>124 komentß°∙ </div></li>
<li><a href='http://interval.cz/redirect/redirect.asp?what=interval_offline&url=http://interval.cz/clanek.asp?article=2714'>Prvnφ Dobr² web v ╚echßch</a><div>87 komentß°∙ </div></li>