Autorizßcia v PHP s MySQL
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.
Ako teoretick² ·vod posl·₧i Φlßnok BezpeΦnost p°edevÜφm - cross-site skripting a session-stealing, 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 uzivatelia, ktorß obsahuje polo₧ky uid (jedineΦnΘ Φφslo u₧φvate╛a), meno (prihlasovacie meno) a polo₧ku heslo, kde budeme uchovßva¥ heslo u₧φvate╛a samozrejme zahashovanΘ - v naÜom prφpade algoritmom md5. SQL prφkaz pre vytvorenie tabu╛ky uzivatelia bude nasledovn²:
uid int(10) unsigned NOT NULL auto_increment,
meno varchar(12) NOT NULL,
heslo varchar(32) NOT NULL,
UNIQUE uid (uid)
);
Pre autorizaΦn² skript budeme potrebova¥ eÜte jednu tabu╛ku autorizacia, ktorß nßm posl·₧i na evidenciu prihlßsen²ch u₧φvate╛ov, konkrΘtne polo₧ky sess (session id), cas (Φas prihlßsenia, resp. Φas poslednΘho prφstupu na server) a polo₧ka user_info 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 autorizacia:
aid int(10) unsigned NOT NULL auto_increment,
sess varchar(32) NOT NULL,
cas int(10) unsigned DEFAULT '0' NOT NULL,
user_info varchar(32) NOT NULL,
UNIQUE aid (aid)
);
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:
$IPadresa.="@".$HTTP_X_FORWARDED_FOR;
$IPadresa.="@".$HTTP_FORWARDED;
$IPadresa.="@".$HTTP_CLIENT_IP;
$IPadresa.="@".$X_HTTP_FORWARDED_FOR;
╚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 getVisitorIdentifier, ktor· nßjdete na strßnke identifier PHP functions by Marc Meurrens, Φ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).
Kontrola IP adresy poΦas doby prihlßsenia m⌠₧e sp⌠sobova¥ problΘmy dial-up u₧φvate╛om, u ktor²ch sa IP adresa menφ dynamicky pri ka₧dom pripojenφ. 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¥.
JedineΦnß identifikßcia u₧φvate╛a (resp. poΦφtaΦa) nie je mo₧nß, avÜak k funkcii getVisitorIdentifier m⌠₧eme doplni¥ ako dodatoΦn· informßciu typ prehliadaΦa, Φφm zv²Üime presnos¥ identifikßcie. Preddefinovanß premennß $_SERVER['HTTP_USER_AGENT'] obsahuje re¥azec identifikuj·ci prehliadaΦ, napr. Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.1) Opera 5.12 [en].
Na ukß₧ku fungovania autorizßcie si vytvorφme nasledovnΘ s·bory:
- index.php - hlavnß strßnka (cez t·to strßnku vkladßme obsah ∩a╛Üφch strßnok, cez require)
- head.php - hlaviΦka strßnky
- foot.php - pΣta strßnky
- settings.php - nastavenia
- login.php - formulßr pre prihlßsenie
- autorizacia.php - funkcia pre autorizßciu
- dalsia1.php, dalsia2.php, dalsia3.php - s·bory, ktorΘ sa bud· vklada¥ ako obsah strßnky pod╛a zvolenΘho menu
S·bor index.php obsahuje funkciu Autorizacia, 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 SetCookie, 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 UÜet°ete a₧ 80 % datovΘho p°enosu.
ob_start ();
session_start ();
require "settings.php";
require "autorizacia.php";
require "head.php";
if (Autorizacia () == false) {
require "login.php";
require "foot.php";
exit ();
}
switch ($_GET["menu"]){
case "dalsia2":
case "dalsia3":
require $_GET["menu"].".php";
break;
default:
require "dalsia1.php";
break;
}
require "foot.php";
ob_end_flush ();
?>
S·bor head.php obsahuje pripojenie k databßze a menu s odkazmi na ∩alÜie strßnky <a href="index.php?menu=dalsia1<?php echo (SID?"&".SID:"");?>">Dalsia 1</a>, 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.
S·bor foot.php uzatvßra HTML obsah strßnky a spojenie s databßzou. S·bor settins.php obsahuje nastavenia k autorizßcii, v²znam je zrejm² z komentßrov.
// debug mode
define ("MK_DEBUG", true);
// typ prihlasenia (0=select, 1=input)
define ("AUTH_TYPE", 1);
// timeout autoodhlasenia (v sekundach)
define ("AUTH_TIMEOUT", 60 * 60);
// ci bude spravcovske heslo platit pre vsetkych
define ("AUTH_USEADMINPASSWD", false);
// ak bude prazdna tabulka uzivatelia, automaticky sa vytvori user AUTH_ADMINUSERNAME, ktory bude povazovany za spravcu (uid=1)
define ("AUTH_ADMINUSERNAME", "[sprßvca]");
// heslo pre automaticky vytvoreneho spravcu (md5)
define ("AUTH_ADMINPASSWD", "21232f297a57a5a743894a0e4a801fc3");
// platnost cookie (10000 dnφ)
define ("AUTH_EXPIRECOOKIE", 60*60*24*(10000));
?>
S·bor login.php obsahuje formulßr pre prihlßsenie u₧φvate╛a. Na zßklade konÜtanty AUTH_TYPE generujeme HTML formulßr s input alebo option prvkami pre zadanie u₧φvate╛skΘho mena. Ak tabu╛ka uzivatelia neobsahuje ₧iadny zßznam, bude vytvoren² u₧φvate╛ AUTH_ADMINUSERNAME s heslom AUTH_ADMINPASSWD. Premennß $username, zφskanß cez $username = $_POST["username"] ? $_POST["username"] : $_COOKIE["username"];, 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.
$username = $_POST["username"] ? $_POST["username"] : $_COOKIE["username"];
?>
<strong>Autorizßcia u₧φvate╛a</strong>
<form method="post" action="index.php" name="formular">
<input type="hidden" name="PHPSESSID" value="<?php echo session_id ()?>" />
U₧φvate╛ : <?php echo HTMLlogin ($username)?><br />
Heslo : <input type="password" name="passwd" />
<input type="submit" value="Prihlßsenie" />
</form>
<script type="text/javascript">
function Focuzz () {
<?php
if (empty($username))
echo "document.formular.username.focus ()\n";
else
echo "document.formular.passwd.focus ()\n";
?>
}
window.onload = Focuzz
</script>
Funkcia generuj·ca formulßrov² prvok pre zadanie u₧φvate╛skΘho mena, pod╛a konÜtanty AUTH_TYPE:
function HTMLlogin ($username) {
$query = "SELECT * FROM uzivatelia ORDER BY meno";
$result = mysql_query ($query) or die();
// ak neexistuju uzivatelia, vytvor spravcu systemu s heslom
if (mysql_num_rows ($result) == 0) {
$result = mysql_query ("INSERT INTO uzivatelia (meno, heslo) VALUES ('".AUTH_ADMINUSERNAME."', '".AUTH_ADMINPASSWD."')");
$result = mysql_query ($query) or die();
}
if (AUTH_TYPE == 0){
$s = "<select name='username'>\n";
while ($row = mysql_fetch_object ($result)) {
if ($username == $row->meno)
$s .= "<option value='$row->meno' selected style='background-color:#ffffff'>$row->meno</option>\n";
else
$s .= "<option value='$row->meno' style='background-color:#e0e0ff'>$row->meno</option>\n";
}
$s .= "</select>\n";
}
else if (AUTH_TYPE == 1) {
$s = "<input type='text' name='username' value='$username' />\n";
}
return $s;
}
?>
S·bor autorizacia.php 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Θ (session_destroy by sp⌠sobilo vymazanie session a session_id 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Θ).
if ($_GET["menu"] == "logout"){
Logout ();
return false;
}
...
...
function Logout () {
// zmaz zaznam so session_id
$result = mysql_query ("DELETE FROM autorizacia WHERE sess='".session_id ()."'") or die();
// odstran session premenne
reset ($_SESSION);
while (list ($key, ) = each ($_SESSION))
unset($_SESSION[$key]);
}
V ∩alÜom kroku vyhodnotφme podmienku isset($_POST["username"]) && isset($_POST["passwd"]) && empty($_SESSION["uid"]), 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 $_POST["passwd"] = md5 ($_POST["passwd"]);. Ak bude nastavenß konÜtanta AUTH_USEADMINPASSWD, 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:
// zisti spravcovske heslo
$result = mysql_query ("SELECT heslo FROM uzivatelia WHERE uid='1'") or die();
$row = mysql_fetch_object($result);
$admin_passwd = $row->heslo;
}
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 AUTH_USEADMINPASSWD:
$result = mysql_query ("SELECT * FROM uzivatelia WHERE meno='".$_POST["username"]."' AND heslo='".$_POST["passwd"]."'") or die();
if (AUTH_USEADMINPASSWD && !mysql_num_rows ($result)) {
// zaznam neexistuje, kontroluj spravcovske heslo
if ($admin_passwd == $_POST["passwd"]) {
// heslo je OK - vyber uzivatela "username"
$result = mysql_query ("SELECT * FROM uzivatelia WHERE meno='".$_POST["username"]."'") or die();
return AuthInsert ($result);
}
}
return AuthInsert ($result);
Funkcia AuthInsert 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 GetUserInfo. Funkciu function getVisitorIdentifier 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 SetCookie nastavφme premenn· $username na hodnotu prßve prihlßsenΘho u₧φvate╛a, ktor· vyu₧φvame v s·bore login.php. 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 uid, ktor· vyu₧φvame v spomφnanej podmienke funkcie Autorizacia isset($_POST["username"]) && isset($_POST["passwd"]) && empty($_SESSION["uid"]). Pri ·speÜnom pridanφ zßznamu do tabu╛ky autorizacia vrßti funkcia AuthInsert hodnotu true, inak false - ne·speÜnΘ prihlßsenie. Funkcia HTMLlogout v dobe prihlßsenΘho u₧φvate╛a zobrazφ odkaz n odhlßsenie u₧φvate╛a (menu=logout).
if (mysql_num_rows($result)) {
$row = mysql_fetch_object ($result);
// pridaj session_id a cas prihlasenia do tabulky autorizacia
mysql_query ("INSERT INTO autorizacia (sess, cas, user_info) VALUES ('".session_id ()."', '".time()."', '".GetUserInfo()."')") or die();
// vymaz stare zaznamy
mysql_query ("DELETE FROM autorizacia WHERE cas < ".(time() - AUTH_TIMEOUT)) or die();
setcookie("username", $row->meno, time() + AUTH_EXPIRECOOKIE);
$_SESSION["uid"] = $row->uid;
$_SESSION["username"] = $row->meno;
$_SESSION["ip_full"] = getVisitorIdentifier();
$_SESSION["browser"] = $_SERVER['HTTP_USER_AGENT'];
return HTMLlogout ();
}
return false;
}
function GetUserInfo () {
return md5(getVisitorIdentifier().$_SERVER['HTTP_USER_AGENT']);
}
function HTMLlogout () {
if ($_GET["menu"] != "logout")
echo "<a href='index.php?menu=logout".(SID?"&".SID:"")."'>Odhlßsenie</a>";
return true;
}
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.
// existuje zaznam s takymto session_id ?
$result = mysql_query ("SELECT * FROM autorizacia WHERE sess='".session_id ()."' AND user_info='".GetUserInfo()."'") or die();
// ano existuje
if ( mysql_num_rows ($result) ) {
$row = mysql_fetch_object ($result);
// nevyprsal timeout ?
if (time() - $row->cas < AUTH_TIMEOUT) {
// update casu
mysql_query ("UPDATE autorizacia SET cas='".time()."' WHERE aid='".$row->aid."'") or die();
return HTMLlogout ();
}
// ano vyprsal
else {
mysql_query ("DELETE FROM autorizacia WHERE cas < ".(time() - AUTH_TIMEOUT)) or die();
session_destroy ();
}
}
return false;
Najd⌠le₧itejÜφm bezpeΦnostn²m prvkom autorizßcie bude protokol https. 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 define ("HTTPS_PATH", "https://localhost/autorizacia/"); k s·borom, ktorΘ si ₧elßme ma¥ zabezpeΦenΘ. V downloade zdrojov²ch k≤dov nßjdete tieto odkazy u₧ upravenΘ.
Ukladanie sessions na serveroch obvykle ostßva v prednastavenom adresßri /tmp, 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 session_save_path("session/autorizacia/") eÜte pred volanφm session_start().
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 session_set_save_handler plus ∩alÜie nami definovanΘ funkcie k prßci so sessions. Tieto funkcie m⌠₧eme nßjs¥ na internete, napr. na strßnke Custom Session Handlers in PHP4. Implementßcia pozostßva z vytvorenia tabu╛ky sessions, ktorej Ütrukt·ru nßjdete v s·bore session_mysql.php, a vlo₧enia tohto s·boru pred session_start v index.php.
ob_start ();
require "session_mysql.php";
session_start ();
require "settings.php";
require "autorizacia.php";
require "head.php";
...
...
Pre ukladanie sessions do databßzy je potrebnΘ nastavi¥ direktφvu session.save_handler = user v php.ini, alebo priamo v naÜich skriptoch funkciou ini_set("session.save_handler","user").
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:
<!-- ROZMERY IMG TREBA UPRAVIT PRI ZMENE KONSTANT V ImgRndStr.php -->
<img src="imgrndstr.php<?php echo (SID?"?".SID:"")?>" width="27" height="21"> <input type="text" name="str" size="3" />
S·bor ImgRndStr.php 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):
if ($_GET["menu"] == "logout"){
Logout ();
return false;
}
if (isset($_POST["username"]) && isset($_POST["passwd"]) && empty($_SESSION["uid"])) {
if($_POST["str"]!=$_SESSION["str"])
return false;
unset($_SESSION["str"]);
...
...
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 dl). 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.
Funkciu ImageColorAllocate som rozÜφril o zadßvanie farieb typu #rrggbb, ktorej nßzov je ImageColorAllocateHex.
$rgb = eregi_replace("#","",$rgb);
$r = hexdec (substr ($rgb,0,2));
$g = hexdec (substr ($rgb,2,2));
$b = hexdec (substr ($rgb,4,2));
return ImageColorAllocate ($im, $r, $g, $b);
}
A pre ·plnos¥ k≤d s·boru imgrndstr.php (pri ukladanφ sessions do databßzy vlo₧i¥ pred session_start riadok require "session_mysql.php"; - ak preferujete t·to mo₧nos¥, je vhodnΘ upravi¥ skript tak, aby sme sa k databßze pripßjali len raz):
###########################
// require "session_mysql.php";
###########################
session_start ();
###############################
# konstanty
###############################
$bgcolor = "#cccccc";
$color = "#000000";
$font = 5;
$length = 3;
// 1 = (0..9)
// 2 = (0..9, A..Z)
// 3 = (0..9, A..Z, a..z)
$type = 2;
// kolko budu znaky "skakat" v horizontalnom smere
$y = 6;
###############################
# samotny skript - generator
###############################
header ("Content-type: image/png");
// inicializacia nahodneho generatora (od verzie PHP 4.2.0 nepotrebna)
srand ((double) microtime() * 1000000);
$w = ImageFontWidth ($font);
$h = ImageFontHeight ($font);
$s="";
for($i=0 ; $i<$length ; $i++)
switch (rand() % $type) {
case 0: $s.=chr (rand(ord('0'),ord('9'))); break;
case 1: $s.=chr (rand(ord('A'),ord('Z'))); break;
case 2: $s.=chr (rand(ord('a'),ord('z'))); break;
}
$s = strtr ($s,"0Oo","1Aa");
$im = ImageCreate ($w*$length, $h+$y) or die ("GD error !");
$bgcolor = ImageColorAllocateHex ($im,$bgcolor);
$color = ImageColorAllocateHex ($im,$color);
$m = rand(1,2);
for($i=0 ; $i<$length ; $i++) {
if ($i)
$m = $m>1 ? 0 : rand(-1,0);
ImageChar ($im, $font, $w*$i+$m, rand() % $y, $s[$i], $color);
}
ImagePng ($im);
$_SESSION["str"] = $s;
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Φφ.
S·bory, potrebnΘ k rozbehaniu autorizßcie, s· vßm k dispozφcii.