Generátor náhodných hesel (10.04. 2008)
Nedávno jsem hledal generátor hesel v php. Stačilo pověřit google a hnedle se na mě valila hromada výsledků. Jaké však bylo mé zděšení, když jsem na otevřel první stránku http://ivorius.com/weblog/skript-na-generovani-nahodnych-hesel Rozhodl jsem se, že vám ukážu, jak to dělá Drobek. Poznámka: vzhled kódu je optimalizovaný pro Operu (a firefox) na optimalizaci pro rozmary IE nemám čas ani chuť.
Původní kód včetně původních poznámek. Chybné řádky jsou podsvíceny červeně. Někdy jde jen o připomínku či drobnou změnu, jindy jsou však chyby zásadní (co se efektivity skriptu týče) a začátečníkům (pro které údajně autor píše) mohou posloužit leda jako odstrašující příklad.
0: function Random_Password($length){ 1: srand((double)microtime()*1000000);2: // inicializace random generátoru 3: $possible_charactors = "abcdefghijklmnopqrstuvwxyz123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 4: //možné znaky 5: $string="";6: while(strlen($string)<$length) {7: // opakuj cyklus dokud počet znaků string je menší než length 8: $string .= substr($possible_charactors, rand()%(strlen($possible_charactors)),1);9: // vybereme náhodný znak z množiny znaků pro heslo 10: } 11: return ($string); 12:}
Nyní rozeberu jednotlivé chyby pěkně řádek po řádku:
- Řádek 1: od verze php 4.2.0 se již srand aktivuje automaticky. Drobná chybka.
- Řádek 5: Opět drobná chybka, tentokrát se to projeví i v efektivnosti. Dvojité uvozovky se používají jen v případě, že chceme převádět např. "\n" na nový řádek. Jindy je vždy efektivnější napsat jednoduché uvozovky a případně řetězit pomocí tečky. není to velká chyba, ale takovýto dobrý zvyk vám může v budoucnu ušetřit (v některých aplikacích stále ještě cenné) mikrosekundy.
- Řádek 6: Tak tady si autor zvolil velice nešťastně jak cyklus, tak opakovací podmínku.
Vždyť on by při každém průchodu cyklem zjišťoval délku nově vznikajícího řetězce (kterou v každém cyklu inkrementuje) a porovnával ji s požadovanou délkou hesla. Vždyť generujeme od nuly, pokaždé jeden znak, takže víme, kolikrát cyklus proběhne. - Řádek 8: další veliká hrůza a to z několika důvodů:
- funkce rand() se již po 32768 použitích opakuje a generuje stejná hesla. Není proto pro případného útočníka problém nechat si vygenerovat 2!! hesla a dopočítat hesla předchozích uživatelů. Lepší je použít funkci mt_rand() která má stejné parametry, ale je rychlejší a využívá jiných (a bezpečnějších) algorytmů.
- Proboha proč používat substr? Vždyť možné charaktery máme v proměnné a proměnná typus tring je co? Pole charů. Proč tedy zbytečně vytvářet jednoprvkový řetězec na jeden znak a poté jej převádět na znak a přidat do řetězce, když můžeme jednoduše získat přímo n-tý znak bez vytváření podřetězce.
Pokud se vám uvedené chyby (hlavně cyklus a zjišťování podřetězce) zdají býti zanedbatelnými, doporučuji vám vrátit se k základům jazyků jako je c (nikoliv c++), abyste si uvědomili, jak věci v php fungují. To že nemusíte deklarovat proměnné a máte k dispozici hromady funkcí je výhodou pouze do té doby, dokud vám to zaručuje luxus a nikoliv prasácky napsané pomalé a neefektivní kódy o kterých vlastně ani nevíte, jak vevnitř fungují.
Následuje mnou přepracovaný kód, který vykonává totéž:
0:function Rand_pass($delka_hesla) { 1: $mozne_znaky = 'abcdefghijklmnopqrstuvwxyz123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 2: $vystup = ''; 3: $pocet_moznych_znaku = strlen($mozne_znaky)-1; 4: for ($i=0;$i<$delka_hesla;$i++) { 5: $vystup .= $mozne_znaky[mt_rand(0,$pocet_moznych_znaku)]; 6: } 7: return $vystup; 8:}
Efektivita se tím z původních zhruba (N!)*M operací substr snížila na N krát přístup k I-tému prvku pole, kde I<=M Toto je samozřejmě pouze hrubý odhad, nikoliv výpočet.
Komentáře k článku
Nebojte se napsat, že se vám článek líbil, nebo nelíbil a proč. Rád si přečtu i váš názor ;)
Systém podporuje Gravatary. Email je nutné vyplnit, avšak nezobrazí se.
darktatka
04.01. 2009 22:23
ja vim ze to je pase, ale tak me prastilo do oci ty possible_charactors. possible_characters je snad klicovy slovo?
Jinak ja sem generovani hesel resil trochu vic s rozmachem - http://github.com/DarkTatka/password_generator/tree/master (samozrejme v ruby)
Drobek
03.10. 2008 07:31
www stránky: http://drobek.nadrobeny.net
Děkuji. Je možné, že jsem si toto neuvědomil. Nejspíš jsem si nebyl jistý, zda mt_rand vrací i horní mez.
Jiří Darmovzal
02.10. 2008 16:56
www stránky: http://www.darmovzal.cz
Na řádce 3 doporučuji odečíst z $pocet_moznych_znaku 1, předejdete tím vybráním znaku mimo index $mozne_znaky funkcí mt_rand na rádce 5.
BKŮV 

