Autor Zpráva
Petr Svetr
Profil *
ahojte,

...mam takovyto formular: svetr.freehostia.com a (blahove) si o nem myslim ze plni me predstavy o opravdu bezpecnem log-in systemu. Pouzivam sessions. Overim existenci jmena a u nej heslo a nastavim sessions. Ve skriptu neresim registraci, databaze, jde mi jen o "prolomitelnost". Udelal jsem to opravdu velmi primitivne. Neni resen slovnikovy, hruby (a co ja vim jaky jeste) utok.

Pocet moznych chybnych zadani hesla je 6.

( pokusny vstup je log: petr pw: password )

Jak je to tedy s bezpecnosti. Co mohu cekat?

(PHP mne nezivi, tak se omlouvam, jestli takovy dotaz nekoho urazi)

Petr
yderf
Profil
Veľmi by nám pomohol php-čkový kód, akosi som dekompiler (deinterpreter) z HTML zabudol doma ;-).

Mimochodom, slovnikovy = hruby (resp. slovníkový je podmnožina hrubého). Tým, že chybných zadaní hesla je max. 6 to riešiš ;-). Ako si však ukladáš počet chybných zadaní? Cookies asi nie (alebo som ich blbo zakázal)? Či teda session zjavne - stačí F5 a je to po starom ;-).
Petr Svetr
Profil *
Kod je zde:



<?php

/***************************
* *
* inicializace stranky *
* *
****************************/

// prihlasovaci udaje
$heslo["petr"] = "password";

// informace
$zpravaText[0] = "Vítejte!";
$zpravaText[1] = "Nashledanou!";

// chybove hlasky
$chybaText[0] = "Chyba v heslu!";
$chybaText[1] = "Takové uživatelské jméno neexistuje!";
$chybaText[2] = "Uživatelské jméno nebylo zadáno!";
$chybaText[3] = "6 x bylo zadáno špatné heslo. Login bude zablokován (TODO: dořešit ( zatim zadat : ?action=erasecount :)) !";

/********************
* *
* autentifikace *
* *
*********************/

session_start();

if ($action == "login") {
// overi jmeno a heslo a v pripade uspechu zapise session
if (strlen($name) > 0) {
if ($heslo[$name]) {
if ($heslo[$name] == $pw ) {
$_SESSION['prihlaseno'] = true;
$_SESSION['uzivatel'] = $name;
$_SESSION['count'] = 0; // Premazani pocitadla spatne zadanych hesel

$zprava = $zpravaText[0]; // OK - overeni proslo !
} else {
$chyba = $chybaText[0]; // CHYBA - overeni neproslo kvuli spatnemu heslu !

if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
} if ($_SESSION['count'] == 5 ) {
$chyba = $chybaText[3]; // 6 x bylo zadáno špatné heslo
$fatalError = true;
} else {
$_SESSION['count']++;
}
}
} else {
$chyba = $chybaText[1]; // CHYBA - jmeno neexistuje !
}
} else {
$chyba = $chybaText[2]; // CHYBA - jmeno nebylo zadano !
}
} elseif ($action == "logout") {
// odhlasi uzivatele
$_SESSION['prihlaseno'] = false;
$_SESSION['uzivatel'] = false;

$zprava = $zpravaText[1]; // OK - odhlášeno !
} else {
$zprava = false;
$chyba = false;
}

if ($action == "erasecount") {
$_SESSION['count'] = 0;
}

?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1250">
<meta name="generator" content="PSPad editor, www.pspad.com">
<title></title>
</head>
<body>
<!-- stavovy box prihlaseni -->
<div style="border: 1px solid silver; margin: 5px; padding: 5px; ">
Uživatel: <?php if ($_SESSION['uzivatel']) { ?>
<strong> <?php echo $_SESSION['uzivatel']; ?> </strong>
/ <a href="?action=logout">odhlásit</a> <?php }
else { ?> <strong>nepřihášen</strong> <?php } ?>
</div>
<?php
if ($zprava) {
?>
<div style="border: 1px solid green; margin: 5px; padding: 5px; color: green; ">
<!-- zprava -->
<?php echo $zprava; ?>
</div>
<?
}
if ($chyba) {
?>
<div style="border: 1px solid red; margin: 5px; padding: 5px; color: red; ">
<!-- chyba -->
<?php echo $chyba;
if ($chyba == $chybaText[0]) {
?>

<br />Počet špatně zadaných hesel: <?php echo $_SESSION['count']; } ?>
</div>
<?
}
?>
<div style="background: silver; margin: 5px; padding: 5px; padding-top: 10px; padding-bottom: 20px; ">
<!-- obsah -->
<?php
if ($_SESSION['prihlaseno'] == true) {
// prihlaseno
?>
<div class="menu">
<a href="?link=1">1 položka</a>
<a href="?link=2">2 položka</a>
<a href="?link=3">3 položka</a>
<a href="?link=4">4 položka</a>
</div>
<?php
} else {
// neprihlasneno
?>
<form action="?action=login" method="post">
<table border="0">
<tr>
<td>Jméno:</td>
<td><input type="text" name="name" value="<?php if ($chyba == $chybaText[0]) { echo $name; } ?>" /></td>
</tr>
<tr>
<td>Heslo:</td>
<td><input type="password" name="pw" /></td>
</tr>
<?php if ($fatalError != true) { ?>
<tr>
<td></td>
<td><input type="submit" value="Přihlaš mne" /></td>
</tr>
<?php } ?>
</table>
</form>
<?
}
?>
</div>
</body>
</html>


yderf: pocitani chybnych zadani hesla je take pres sessions. Tve vete : stačí F5 a je to po starom ;-) - to znamena ze to sessions premaze? vazne...? To preci ne.
yderf
Profil
Hej premaže sa, resp. skončí jej platnosť. Ak to chceš robiť takto, tak ukládaj Cookie, alebo IP, obidve možnosti majú svojú nevýhody, ale Cookies sú použiteľné, ak ukladáš Cookie, ktoré testuje, či Cookies vôbec sú povolené (osobne to však takto nerobím).

Ale teraz k skriptu:
Prihlasovanie podľa mňa bezpečné nie je. Po prvé, nepoužívaš $_POST, čo je dosť veľká blbosť - takto ti môžem poslať premenné cez hocičo od URL (GET), ale aj vlastné Cookies. Z toho vyplýva SQL Injection až sa bude pariť (teraz to nemáš napojené na DB, ale obyčajne to tak je). V každom prípade používaj superglobálne polia (teda $_POST, $_GET, presne ako používaš $_SESSION).

Hlavne však prečo to nie je bezpečné je to, že heslá bežia cez http pekne krásne plain texte ;-). To znamená, že ich odsniffuje skoro hocikto. Buď používaj https, alebo ich šifruj javascriptom pred odoslaním (knižnice na to si nájdeš Googlom).

A ešte jedna vec, pri Session musíš používať phpsessid v url (alebo niečo podobné, najlepšie asi jeho hash) - inak ti session nemusí fungovať (aspoň takto bolo keď som naposledy so sessions robil :-) )
TFSi
Profil
Podle mého je to pro "běžné" zabezpečení dostatečné.
Jediné co, tak:
- názvy sezení ještě prohnat přes MD5 nebo tak něco
- neplatná přihlášení ukládat jinak než do sezení (já využívám obdobný princip, ale neplatná přihlášení zapisuji do db - každou hodinu maximálně 5 neplatných přihlášení na uživatele)
- zapracovat na zdrojáku (oddělit HTML od PHP atd... )

EDIT + ještě to šifrování hesla při odesílání. Já na to používám JS odsud: http://pajhome.org.uk/crypt/md5/md5src.html
Petr Svetr
Profil *
yderf: dikk. Jen jeden hloupy dotaz: sifrovani pres javascript mohu udelat, ale k cemu to bude kdyz si ho klient vypne? Https na serveru, kde bych tohle potrbeoval nezapnu...
Petr Svetr
Profil *
TFSi:
1) ok
2) ok
3) zde je to schvalne splacle do jednoho bordylku... tak to samozrejme nezustane

Diky
yderf
Profil
Petr Svetr
jasne, ak ho vypne, tak ho vypne, je to jeho problém. Je dobré ho na to upozorniť a potom je to už v jeho rukách (prehliadači, hlave a pod.). Javascript je normálny nástroj ako riešiť veci, avšak základna funkčnosť musí byť aj bez neho. A šifrovanie hesiel je fakt vec, ktorá pomôže.

A len tak mimochodom, odkaz na štatistiku práve po ruke nemám, ale JS má zapnutý >99% užívateľov, čo je v zásade celkom dosť :-).
Mastodont
Profil
Petr Svetr

Zkus zvážit třeba to, že kdo bude mít JS vypnut, přihlásí se jen s omezenými právy.
krteczek
Profil
šifrovat v js (podle tadytoho kontextu se jedná o md5 hash => není to oboustranná šifra) je stejná blbost jako nešifrovat vůbec, protože na serveru to js-šifrované heslo použiješ jako heslo, takže kdo odchytí komunikaci má klíč..

Rady na závěr:
použij takové prostředky, které odpovídají nasazení aplikace => je zbytečné mít na blogu https, ale u bankovních operací je to nutnost, pamatuj že certifikát není levný, a pokud je vystaven na někoho jiného (hosting) vždycky se objeví hláška o tom že to není ten kdo to má být
Použíj generovaná hesla o minimalne 8 znacich =>kombinace malých, velkých písmen a číslic + další znaky podle uvážení
dej si pozor na možnost záměny písmen (O a 0, ...)
yderf
Profil
krteczek

To hej, avšak aspoň heslo ostane v tajnosti.
P_T_
Profil
Já bych měl jenom takovou poznámečku. Zrušil bych hlášky upozorňující na špatné jméno, nebo heslo a nahradil bych to jednou neurčitou hláškou ( např. přihlášení se nezdařilo ... ). Tímto způsobem totiž potencionálního útočníka informuješ, že už uhádnul jméno a "stačí" mu už jen heslo. ;-)
Mastodont
Profil
krteczek
[šifrovat v js je stejná blbost jako nešifrovat vůbec, protože na serveru to js-šifrované heslo použiješ jako heslo, takže kdo odchytí komunikaci má klíč

Nesmysl, viz http://php.vrana.cz/bezpecne-prihlasovani-uzivatelu.php
Petr Svetr
Profil *
Dekuju vsem...
los
Profil *
Vďaka tomu, že je zapnuté register_globals, tak je to deravé. Napr. keď pošlem požiadavku, ktorá nastaví heslo[test]=t, tak sa potom viem prihlásiť pod menom 'test' s heslom 't' a je jedno, či je JavaScript povolený alebo nie.

Pre otestovanie stačí nastaviť atribút action formulára s prihlásením na "?action=login&heslo[test]=t".

Pred používaním premenných ich vždy nainicializuj ($heslo = array()).
TFSi
Profil
krteczek
šifrovat v js ... je stejná blbost jako nešifrovat vůbec

Jak kdy. Já třeba tam přidávám ještě salt a i kdyby útočník výsledný hash zachytil, tak mu bude k ničemu, protože už bude nepožitelý :)
Petr Svetr
Profil *
TFSi: co to je salt? Omlouvamse za uplne pitomou otazku :( Leda mne napadlo, ze by js nejak pridal ke jmenu cas a ten pak zasifroval... v php by se pak taktez k heslu pridal cas a pak se porovnalo. Ale to asi blouznim ze?

Diky moc za Vas cas... vsem
Petr Svetr
Profil *
los: vyreseno
Petr Svetr
Profil *
TFSi: beru zpet, skusim to (http://cz.php.net/manual/cs/function.crypt.php)
TFSi
Profil
Petr Svetr salt, neboli lidově řečeno sůl je řetězec, který připlácneš na začátek nebo na konec hesla a potom co celý protáhneš MD5-kou (nebo SHA1, nebo čím budeš chtít). Třeba já to dělám tak, že přidám IP adresu a ještě řetězec vygenerovaný na serveru, takže potom nemám MD5('moje_tajne_heslo'), ale MD5('moje_tajne_heslo255.255.255.255abcd'). No a i kdyby útočník získal tento hash, tak mu bude k ničemu protože
a) prolomením hashe nezíská řetězec, jehož hash bude stejný jako ten v mé db
b) nebude mít IP adresu stejnou jako v hashi a kdyby jí podvrhnul, tak ještě bod c
c) server si pamatuje, jaký řetězec poslal pro který požadavek a ikyby se moc snažil, tak získat tento řetězec bude minimálně hodně těžký.

==> i kdyby to neodradilo jó profíky, tak kdejaký patlaly, kteří sedí s notebookem na wifi určitě ;)
krteczek
Profil
TFSi: stále nevidíš tu chybu? heslo =>javascriptMd5(heslo) => nezabezpečený přenos => server přijme md5hash hesla => ověří jestli je md5 hash hesla rovno záznamu v databázi, čili ten md5hash hesla je heslo, kdokoliv ho odchytí může ho použít, je to heslo, ikdyž má 32 znaků a špatně se pamatuje.

Podle mne je to zbytečně vynaložená námaha, systém není bezpečnější, jen vytváří iluzi bezpečnějšího.

Vždycky je třeba se zamyslet nad tím jestli se vynaložené prostředky vrátí (vyplatí), viz můj předešlý příspěvek
WertriK
Profil
krteczek, popř. kdokoli jiný
Jen tak ze zajmavosti, jak by jsi řešil situaci zabezpečení kdyby jsi nemohl použít https ?
Jednalo by se o nějakou stránku, ne blog ale ani ne o nějakou bankovní chráněnou stránku.
Jde mi o nějakou střední bezpečnost.
krteczek
Profil
WertriK: pokud jde o přihlášení, asi bych použil to řešení od Jakuba Vrány, koukal jsem se až teď. Jenže přihlášení není vše, je třeba zajistit i ostatní provoz jako je http://php.vrana.cz/cross-site-scripting.php a jiné podobné legrácky.
TFSi
Profil
krteczek
stále nevidíš tu chybu? Nevidím ... :)
Buďto to blbě chápu já a nebo ty. Ano, použití javascriptMd5(heslo) je pouhá "iluze bezpečí", ale se javascriptMd5(heslo+salt) už není pouhá iluze!
Ano, jakožto útočník odchytíš hash, ale pokud bude generován i se saltem (který se mj. pořád mění díky serveru a IP), tak ti bude k ničemu, protože i pro jedno heslo bude k pokaždé jiný hash, který bude možné použít pouze jednou. A protože tebe, jakožto útočníka uživatel vždy předběhne, tak se budeš moci jít klouzat i kdyby jsi nakrásně znal hash, který se od uživatele odešle. A důvod? Hash už je neplatný, protože server ví, že na požadavek s ID=abc už bylo heslo odesláno a tudíž není platný.

Ale jinak jak jsi psal: přihlášení není všechno.
krteczek
Profil
TFSi: v předešlém příspěvku jsem se zminil o tom že jsem se na to koukl až nakonec, po přečtení jsem uznal, že je to vychytane a zmínil jsem se také o tom, že bych tento způsob asi i použil. Navíc jsem dodal, že nejde jen o přihlášení, ale o další možnosti napadení aplikace, a proti nim je třeba se také bránit. Ochránit jen přihlášení nestačí.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: