Zabezpečení PHP skriptu heslem II.

23.09.2001 20:20 | blackhole

HTTP autentifikace
Co to je? Je to jeden ze způsobů jak zabezpečit své stránky před "nezvaným
hostem". Je to poměrně jednoduché a účinné. Jak probíhá HTTP autentifikace?
Server obdrží od scriptu požadavek na údaje v určité relaci (skupina
stránek). Ten se na tato data otáže prohlížeče, který má stránku zobrazit.
Pokud browser tato data má, odešle je. A pokud tato data nemá, vyžádá si
je. Nemusíte psát žádné přihlašovací formuláře. Objeví se "windousowské"
okno, které vyplníte. Prohlížeč si tato data pamatuje, dokud je spuštěný.
Vždy, když si je server poté vyžádá, browser je poskytne, aniž by znova
zobrazoval okno pro zadání údajů. Pokud chcete HTTP autentifikaci použít na
domácím serveru, je nutné mít nainstalované PHP jako modul, tzn. PHP4.

HTTP autentifikace předává tři proměnné. Pro uživatelské jméno je to
proměnná $PHP_AUTH_USER a pro heslo proměnná $PHP_AUTH_PW. Předává se ještě
proměnná $PHP_AUTH_TYPE, která obsahuje typ autentifikace. S tou však
nebudeme pracovat. Ukážeme si na vysvětlenou malý příklad: Pokud je v
proměnné $PHP_AUTH_USER uložena hodnota "dovolena" a v proměnné
$PHP_AUTH_PW hodnota "fotky", zobrazí se nápis "Dovolená 2001". Pokud ne,
zobrazí se hláška o neautorizovaném přístupu, error 401. Prohlédněte si
nyní zdrojový kód, detaily vysvětlím hned po něm.<?
if($PHP_AUTH_USER==\"dovolena\" && $PHP_AUTH_PW==\"fotky\"):
    Header(\"Cach-Control: no-cache\");
    Header(\"Expires:\".GMDate(\"D, d M Y H:i:s\").\"GMT\");
    Header(\"Pragma: No-cache\");
    ?>
    <HTML>
    <HEAD>
    <TITLE>Zabezpečení PHP skriptu heslem - HTTP autentifikace</TITLE>
    </HEAD>
    <BODY>
    Dovolená 2001
    </BODY>
    </HTML>
    <?
else:
    Header(\"HTTP/1.0 401 Unauthorized\");
    Header(\"WWW-Authenticate: Basic realm=\\\"dovolena_2001\\\"\");
    echo \"Neautorizovaný přístup\";
    exit;
endif;
?>Co jednotlivé řádky znamenají? První řádek zkontroluje, zda vyžádané údaje
souhlasí s těmi, které povolí přístup. Pokud jsou shodné, druhý, třetí a
čtvrtý řádek předají informace o tom, aby prohlížeč neukládal stránku do
cache. To z toho důvodu, aby si ji nikdo nemohl prohlédnout, aniž by zadal
správné jméno a heslo. Pak následuje HTML kód vlastní stránky, kde - v
našem případě - budou fotky z dovolené.

A pokud se zadané údaje neshodují? Řádek Header("HTTP/1.0 401
Unauthorized"); znamená následující: HTTP je název protokolu, se kterým se
pracuje, 1.0 je verze protokolu, 401 je číslo chyby a Unauthorized její
slovní vyjádření. Další řádek, Header("WWW-Authenticate: Basic
realm=\\"dovolena_2001\\"");, určuje typ autentifikace, zde Basic. No, to je
trochu zavádějící, protože, pokud je mi dobře známo, jiný typ zatím ani
není podporován. Realm určuje typ stránek, pro které dané údaje platí. To
znamená, že na všech dalších stránkách, které budou obsahovat fotky z
dovolené, se stačí otázat zda exsistuje proměnná $PHP_AUTH_USER (viz níže).
Hláška "Neautorizovaný přístup" se v tomto scriptu zobrazí po stisku
tlačítka "Storno" na přihlašovacím formuláři. Příkaz exit je ekvivalentem
příkazu break, a souží k okamžitému ukončení scriptu, ať se za tímto
příkazem (nikoliv funkcí) píše cokoliv. Když by script vypadal takto,
zobrazovalo by se okno na zadání hesla tak dlouho, dokud by nebylo vše v
pořádku. První řádek lze samozřejmě nahradit dotazem do databáze, kde se
porovnají jednotlivé položky. Spojíme s databází (první řádek), vybereme
tu, kde se nachází tabulka s našimi údaji (druhý řádek). SQL příkaz vybere
z tabulky hesla takovou položku pass, která je ve stejném záznamu jako
login (který obsahuje hodnotu proměnné $PHP_AUTH_USER). Pak načteme
výsledek do pole (čtvrtý řádek). A poté jen porovnáme shodnost hesel.<?
$spoj=MySQL_Connect(\"počítač\",\"uživatel\",\"heslo\");
MySQL_Select_DB(\"jmenodatabaze\");
$vys=MySQL_Query(\"SELECT pass FROM hesla WHERE login='$PHP_AUTH_USER'\",
$spoj);
$v=MySQL_Fetch_Array($vys);
if($PHP_AUTH_PW==$v[\"pass\"]):
?>Pokud však použijeme dotaz do databáze, měl by se script upravit takto:<?
if (!isset($PHP_AUTH_USER)) // pokud neexsistuje $PHP_AUTH_USER provádí se
tato větev scriptu
{
  Header(\"HTTP/1.0 401 Unauthorized\");
  Header(\"WWW-Authenticate: Basic realm=\\\"dovolena_2001\\\"\");
  echo \"Neautorizovaný přístup\";
  exit;
}
else // jinak se provádí tato větev
{
  // nebudeme ukládat do cache
  Header(\"Cach-Control: no-cache\");
  Header(\"Expires:\".GMDate(\"D, d M Y H:i:s\").\"GMT\");
  Header(\"Pragma: No-cache\");
  // provedeme kontrolu hesla
  $spoj=MySQL_Connect(\"localhost\");
  MySQL_Select_DB(\"uzivatele\");
  $vys=MySQL_Query(\"SELECT pass FROM uzivatele WHERE
login='$PHP_AUTH_USER'\", $spoj);
  $v=MySQL_Fetch_Array($vys);
  if($PHP_AUTH_PW==$v[\"pass\"]): // pokud se heslo shoduje zobrazíme správné
informace
?>
    <HTML>
    <HEAD>
    <TITLE>Zabezpečení PHP skriptu heslem - HTTP autentifikace</TITLE>
    </HEAD>
    <BODY>
    Dovolená 2001
    </BODY>
    </HTML>
    <?
  else: // jinak vypíšeme hlášku a ukončíme běh scriptu
    Header(\"HTTP/1.0 401 Unauthorized\"); // komentář níže
    echo \"Neautorizovaný přístup\";
    exit;
  endif;
}
?>Teď ten slíbený komentář. Úplně jsem vypustil hlavičku s WWW-Authenticate.
Tato způsobuje, že se okno na zadání údajů objevuje neustále znovu a znovu,
dokud nejsou přístupové údaje správné. Řádek nad tím slouží pro zápis do
přístupového protokolu. Pokud ho script bude obsahovat, zapíše se do
protokolu chyba 401. Když ho tam nedáte, do souboru access.log (na serveru
Apache) se zapíše číslovka 200. To jest bezproblémové vyřešení požadavku
nikoliv pokus o vstup na stránku, na niž neměl uživatel povolení. Toto má
hodnotu pouze pro správce serveru, který má k těmto věcem přístup. Pro nás,
jako uživatele, to nemá příliš velký význam.

Když máme stránek více a chceme je též chránit, měli by vypadat takhle,
přičemž hlavičku můžeme vynechat.<?
if(!isset($PHP_AUTH_USER))
{
  Header(\"HTTP/1.0 401 Unauthorized\");
  echo \"Neautorizovaný přístup\";
  exit;
}
else:
{
... obsah stránky se kterou se pracuje pokud je uživatel autorizovaný ...
}
?> Starší verze IE neporozuměly správně hlavičkám vyžadujícím autetifikaci. V
těch případech musíme volání funkcí Header() prohodit. Výsledkem je sice
hlavička, která neodpovídá standartu HTTP, ale poradí si s ní i starší
verze IE.

Měli by jsme však uživatele upozornit, že pokud ke spuštěnému prohlížeči
přijde jiná osoba, může si stránky v klidu procházet. Jedna možnost je
vypnutí browseru, což je nejspolehlivější. Další možnost je implementování
(zabudování) odhlašovací funkce do scriptu. Funkce by po stisku talčítka
měla prohlížeči odeslat informaci o neautorizovaném přístupu, i když je
jméno a heslo správné. To by mělo prohlížeč donutit k zapomenutí těchto
informací.
Další možností je do "realm" uložit jednoznačný identifikátor, který bude
platný pouze do doby, než se uživatel z aplikace odhlásí.
Pak také můžeme zařídit, že se "realm" bude v určitých intervalech měnit.
To však nelze použít osamoceně, protože do jednoho časového okamžiku se
může vměstnat odchod uživatele a přístup neznámého útočníka. Tato metoda
však částečně řeší problém, kdy se zapomene uživatel odhlásit. Pokud se
změní "realm", objeví se požadavek na nové zadání jména a hesla. Časté
zadávání údajů však může některé uživatele znechutit.

Doufám, že vše bylo srozumitelné a jasné. Teď už si vaše fotky z letošní
dovolené může prohlédnout jen ten, komu to vy sami dovolíte.tomash