Opakované hašování
Školení, která pořádám
Už jsem psal o tom, jak rychle se dají vypočítat haše krátkých hesel. Pokud nechceme uživatele nutit k používání dlouhých hesel, tak bychom potřebovali hašování nějak zpomalit. Jednoduchý způsob je volat hašovací funkci opakovaně:
<?php
$hash = $_POST["password"];
for ($i=0; $i < 1000; $i++) {
$hash = md5($hash);
}
?>
Tomuto postupu se říká zesilování hesla. O tomto postupu se zmiňuje i článek o ukládání hesel vydaný v rámci měsíce bezpečnosti PHP. Místo funkce md5 se dá volat i jiná funkce, můžeme přidat salt, to ale v tuto chvíli není zajímavé.
Teoretická komplikace
Při opakovaném hašování může teoreticky docházet ke vzniku cyklů. Když by nějaká primitivní hašovací funkce hašovala a1
na b2
, b2
na c3
a c3
na a1
, tak by všechna hesla, která se v některé fázi opakovaného hašování zahašují na jeden z těchto kódů, splynula v pouhá tři různá hesla. Jinými slovy by se podstatně zvýšilo riziko kolize.
Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací.
Diskuse
honza:
opakovane hashovani ma velky vyznam v pripade potencionalniho utoku. Pri slovnikovem utoku bude utocnik muset provest 100x operaci coz ho vyrazne spomali.
vasio:
Dovolim si nesouhlasit, při slovníkovém útoku by to mělo být jedno jestli je to 100x nebo 1000000x hašovaný, při přihlašování se to přeci nijak neprojeví snad v mikrosekundách na odezvě serveru ;-), omluvte mně pokud se mílím, možná jsem mimo :)
vasio:
Pardón, vy řešíte situaci kdy útočník již hash má, sorry za můj nepředmětný komentář.
Chtěl jsem se zeptat, k čemu je důležité/výhodné zpomalit samotný proces hashování?
Když má útočník seznam hašů a snaží se z nich zjistit původní heslo, tak to může udělat hrubou silou – zkouší buď všechny kombinace povolených znaků nebo jede podle nějakého slovníku. Při jednoduchém hašování stihne vyzkoušet třeba milión hesel za sekundu, při tisícinásobném jen tisíc. Sníží se tedy počet hesel, které je schopen v daném čase odhalit.
Nezdá sa mi že 1000-násobné zahashovanie je dobrý nápad ako "spomaliť hašovanie". Ono stačí spraviť hash pár-krát a spomalenie zabezpečiť inak, napríklad použitím funkcie sleep():
<?php
$hash=md5(md5($password).$salt);
sleep(1);
?>
Aha, podľa tvojho predchádzajúceho komentára ti ide o situáciu, kedy útočník má prístup k dátam z databázy a vie aj akým spôsobom je hash vytváraný (čiže PHP skript). V tom prípade je môj komentár bezpredmetný.
majak:
V tomto článku sa píše, že na účely uchovávania hesiel je vhodné použiť funkciu bcrypt:
http://codahale.com/how-to-safely-store-a-password/Jej výhodou je, že hashovanie trvá dlhšie (dá sa nastaviť "work factor") a zároveň by sa tým nemalo zvýšiť riziko kolízie.
Nevýhodou je, že táto funkcia je v PHP určite dostupná až od verzie 5.3, v starších jej dostupnosť závisí od systému.
Konkrétne sa jedná o funkciu crypt, typ Blowfish:
http://php.net/manual/en/function.crypt.php
paranoiq:
pokud jsem to správně pochopil, tak work factor v podstatě znamená počet opakování ($rounds=5000). např. samotná blowfish je v základu pouze dvakrát pomalejší než SHA-2 (
http://www.cryptopp.com/benchmarks.html). není tedy důvod spoléhat na přítomnost téhle knihovny
Anebo tedy mcrypt, který je od PHP4.0.2 a snad i běžně dostupný, alespoň většinou ho vídám povolený (na hostinzích)
smrt:
"Opakovane hashovani" se pouziva pri sifrovani lokalnich souboru (treba gpg) nebo disku (treba truecrypt). Delat tohle na serveru muza jen hovado, co si neuvedomuje, ze je ten vypocet casove a -vykonove narocny a server tak neumerne zatizi.
Bohužel jste motivaci pro opakované hašování při ukládání hesel vůbec nepochopil. Jde právě o to, dobrovolně nepatrně zpomalit server při běžném provozu, abychom razantně zpomalili útočníka.
A nemohlo by obmedzeniu cyklov pomôcť niečo takéto?
<?php
define(
'SALT',
'@"w{hV@;)*j94Qcha(bXX`G<JAd<^dHQdcB9[_Eqt-seiKP?=BxZ/V42Jvu^<"-2@Tn@|0Z8@<NZ}$+P'.
't!:\[-c@Bo+)7/qJ"%>m8FP<mV(^!fcFI.%L]^,)(#kVI-SoWEh]psR&OBGD6l$/h|$EZ<]m"Lu&7Xw{'.
'2=D^glD$}7/zx_-Td~w7&r.@PWNDY7p=\"FS=^]Zm.&_7K]~Ts8mu+%C1=W#,GW}$EMoQnGxn,d<h:*t'.
'BQd4|X^?x]A/$p"`.uEgiPIiI42X7:eJ<d#1eh6%zHB;{+uX_~LKO;~/}@fP};EG^~<Q]$k@#r[&}Jr#'.
'E9rt#,;Zm/Bq9<44<{=K|sg,g;\OT{9?MR832Q3#<j~D%kP*C;s|d^kG1QVd5j|Ovu;k7[YBl2*Y(0g+'.
'@VG-8cV4-A10@F.Q3oEqdQQz"e&Mypl1A\#J)Zpt\FpUM4&Hx<DAE=b$thBF?d(UWS2g%kd"w#RvH*b`'.
'K7IBi\2XhS=}]DwlV,L-]b8#|\l<A)ja#%B2>P,/#%4Cy*{=\0uV+Jiy2n/zLvM-HDz"J>]taF/DjyT!'.
'Am.X_u;L(LKz|\{bHuWZF<]I[RZ?e=la|q01^"bfvl&ecrtTfo*=r"9i>$M)h@APR(&51__@G2Y$#-02'.
'#G5OQp,aKii"5/_b1>+\(+LwEp+@7~_g.z,o!nvMYiGTyqfM5C|m.M"I1eX2/"EBpF-a8+8TzPe0ske-'.
'"qM?r:v9q+=t,r4!bwS{~{{t/K?_1cUyn2~|EJV;P-4UebBbD]0&0(D*b?3Rjcyz4>/G{v7^n)TIi_SP'
);
$login = 'root';
$password = 'nbusr123';
$hash = sha1($login.strrev(SALT).$password);
$salt_length = strlen(SALT);
for ($i=0; $i < 10000; $i++) {
$hash = sha1(substr(SALT, -$i%$salt_length, 1).$hash.$password.substr(SALT, $i%$salt_length, 1));
}
echo $hash;
?>
asd:
Vícenásobné hashování je blbost. Hashovací algoritmus se po několikanásobném hashování začne deformovat a vznikají stejné výsledky.
Na hesla používejte spolehlivé hashovací funkce určené k hashování hesel, jako je bcrypt();...
V této podobě bych to nedělal, ale něco v tomto stylu zabrání teoretické možnosti deformace:
<?php
$hash = '';
for ($i=0; $i < 1000; $i++) {
$hash = md5("$input$hash");
}
?>
Samozřejmě ale bude lepší použít funkce, kde to je už vyřešeno. Například tady si nejsem jist, jestli je lepší "$input$hash", nebo "$hash$input". (Protože pravá kongruence hashovacích funkcí a vliv kolizí, length extension attack apod.) Detaily jsou někdy v kryptografii dost podstatné. Nejspíš to zde vyjde nastejno, ale než to řešit, je lepší použít bcrypt. (A vsadil bych se, že většina programátorů by to ani neřešila.)
Diskuse je zrušena z důvodu spamu.