Ruby + pwgen

Sok fajta véletlen jelszó generátor progi létezhet, én általában pwgen parancsot használom Linux alatt. Ugye ez ún. kiejthető jelszavakat is létre tud hozni. Valóban könnyebben megjegyezhető. Mindenhová véletlen jelszót generálok, nem csak magamnak hanem másoknak is, ezért sűrűn használom.

Mindössze annyi volt a gondom vele, hogy bizonyos karaktereket nem akarok hogy megjelenjenek a jelszóban. Olyanokra gondolok, amelyek leírva vagy nyomtatva, betűtípustól függően összekeverhetők. Ilyen a kicsi "i" betű, kicsi "L" betű és nagy "L" betű, "O" betű és nulla stb. Illetve a különböző (pl. qwertz és qwerty) billentyűzet kiosztás miatt olyan jelszavakat akartam, amelyek mindenfajta bemeneten működnek.

Erre dobtam össze egy Ruby script-et password_generator.rb néven.

A kimenete hasonló képpen így néz ki:


Uesucu2h Fatux5amak mKscuK7s N7mB2HAvJh h.}t^{B3 k.Y6N>xews
axe5dEew 3kaekusuCu V5xCxeM4 3uKJ5HfmuD IJsh=&>N dWI{f}--SZ
Uru4refe unusec4Anu 3fduAAmv TmrPN3BK4M IAwuc%@R \=[PtVaV0o
huTua3xe 2maseepuTh uhHhEDr2 V3sCLuVpcD ~axXxk~* [b3DhEGxrd
as5Aupep xauVeheve7 M8Rb4S4A t5DCTbKBRf q:{mDI06 xrCq}Oxyj\
neShua4n ve4Ppasuax Pv4LsFap UkXrX5BUKX :&@D}Y6B ;:=(42X%"y
Ce7sunue arekUcara5 5MV2ebdE DEt83NpAxa |cnYM.2, 5t71lO,M1f
3xaFukam dakuCapu4s uv3mxN3R CAaEfctM4S X[ZMi@`/ '!0H-Y(2\w
akEanu7k tepaSafa3f bkk4uVhv fNpHJ5fncs ?i&BtIiV 8n+\v9(\=,
3atUduta Ssekue4ned Abh2s4cR EUruntB3au gYUXsf(W L+D}0lD`GG

Szerk.: pwgen függőség eltávolítva (köszi hrgy84-nek az ötletet), így már gyors a program.

Szerk.2: alapból inkább nem nyomtatok ki 8 karakternél rövidebb szavakat, hanem 6 oszlopot: 8 és 10 karakteres kiejthető jelszó + 8 és 10 karakteres egyszerű jelszó + 8 és 10 karakteres erős jelszó - egyre erősebbek jobbra haladva.

Plusz ha egy számot megadunk paraméterként, akkor olyan szélességű jelszavakat nyomtat ki három oszlopban, szintén kiejthető, egyszerű és erős jelszavakkal.

Hozzászólások

A pwgen szerintem kivalthato. Arra kell figyelni, hogy ha egy massalhangzo jott ki, akkor a kovetkezo korre tiltsuk le a massalhangzokat (cikluson kivul definialt valtozo), ha pedig le vannak tiltva a msh-k, akkor addig porgessunk, mig mgh nem jon ki, utana engedjuk fel a tiltast. Ehhez eleg a maganhangzok listajat tombben tarolni, ha valami nincs benne, az massalhangzo.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

Igen, ez logikusan hangzik. A tömbben tárolás is jó, így nem kell "pörgetni".

Csak a pwgen az angol kiejtéshez képest generálja valahogy. Tehát most írok egy kitaláltat, pl.: sleevE56 meg ilyenek. Meg használ "sh"-t is, ugye ez "s"-nek ejtve megint egyszerüsít (vagyis a kiejtett szóban kevesebb a hang mint a leírtban, tehát gondolom így egyszerűbb - persze meg kell jegyezni hozzá az eredeti formát).

Szóval még nem látom egyértelműen, hogy a pwgen hogy csinálja. Bár jó lenne utánanézni és tiszta gyors Ruby kódot összedobni.

Nezd, a 1 msh + n mgh modszer mindig mukodik. Ha akarsz sh, dh-t is, akkor egyszeruen felveszed a h-t a maganhangzok korebe. Nem a myspell forrasat modositod :)

Kozben irtam meg, csak ramvalaszoltal, szoval:

TODO: ismerkedj meg a range-kkel:


chars = ((0..9).to_a.collect(&:to_s) + ('A'..'Z').to_s + ('a'..'z').to_s) - %w(o O 0 1 i j l I z Z y Y g q 9)
chars.shuffle! # Kicsit talan jobb lesz az eloszlas random karaktereknel.
# blabla
nextchar = chars[rand(chars.size)]

--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

Igen, tudtam, hogy ez meg magas lesz. :-)

A collect, map, es egyeb fuggvenyek alapbol egy block parametert varnak. Ha &:sym format hasznalod, akkor az tortenik, hogy a kollekcio osszes elemere meghivja a szimbolum nevu parancsot.

Vagyis:


numbers = (0..9).to_a
s = numbers.collect(&:to_s) # <= Ez a kod
s = numbers.collect { |num| num.to_s } # <= Ekvivalens ezzel

Valojaban asszem ez annyival bonyolultabb a valosagban, hogy send-et hiv ra, ami kepes szimbolumot fogadni parameterkent. Ez nehany extrem esetben problemakat okozhat, de rohadt ritkan.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

Elvileg meg van, frissítettem github-on. Ezt kiemelném:

# the rules are:
# - max 2 vowels may follow each other
# - max 2 consonants may follow each other if they match
# - max 1 upper case letter is allowed
# - max 1 number is allowed

Ennek további kiegészítésére esetleg ötlet?

ch_con_double = ["sh", "ch", "th"]

Ugye egyforma mássalhangzóból engedek 2-t, vagy a fenti listából passzolókat.

Teljesen átírtam mindkét algoritmust. A mostanival már nincs jelszó újrakérés (ha az elkészűlt jelszó nem felel meg a feltételeknek), hanem egy menetben lefut az egész, és így gyorsult.

Meg a "Q" betűt is kivettem, mert irásban az is hasonlíthat a nagy "O"-hoz vagy nullához.

Első függvénynél a szabályok:
- minimum 1 kisbetű kell
- minimum 1 nagybetű kell
- minimum 1 szám kell

Másodiknál a szabályok:
- csak 1 nagybetű kell
- csak 1 szám kell
- minimum 1 kisbetű kell
- maximum 2 magánhangzó követheti egymást ha nem egyeznek, kivéve "ee"
- maximum 2 mássalhangzó követheti egymást, ha egyeznek, vagy a kivételi lista alapján

Ezek alapján a fentit is úgy oldottam meg, hogy lekértem 1 kisbetűt, 1 nagybetűt és 1 számot mindenképpen a jelszóba, majd hozzákértem a többit véletlenszerűen, az eredményben pedig összekevertem a karakter sorrendet.

A második is hasonló elv alapján, csak bekavart (nem kicsit) az, hogy a magánhangzókat és mássalhangzókat is külön csoportban kell a végén összekeverni, ráadásul a külön típusok helye megmaradjon - mindez azért, hogy nehogy az összekeverés után egymás mellé kerüljön 2-nél több magánhangzó például. Majd a számot még beszúrtam véletlen szerűen valahova.

A kiejthető jelszavak sajnos ilyen hosszúságban nem jók.

Remélem jól értelmeztem a kódot, nem ismerem a Ruby-t :)

Használható: abcdefhkmnprstuvwx23457 // 23 karakter
Mássalhangzók: bcdfhkmnprstvwx // 15 karakter
Magánhangzók: aeu // 3 karakter
Számok: 23457 // 5 karakter

Szóval, mennyire erős az ezzel generált kiejthető jelszó?

A számokkal és a nagybetűkkel most nem foglalkozunk, majd a végén leírom hogy miért. A mássalhangzók mellett csak magánhangzók állhatnak, ezért így párok jönnek létre. Hány ilyen pár létezhet? Mássalhangzókkal kezdve: 15*3, magánhangzókkal kezdve 3*15, vagyis eddig 15*3*2 párunk van.

Mássalhangzó nem állhat egymás mellett, kivéve ha egyforma (+15), vagy ha ch, kh, ph, sh, th (+5). Magánhangzó sem állhat egymás mellett és két egyforma is csak az ee (+1) lehet. Vagyis összesen tehát 15*3*2+15+5+1 = 111 párunk van.

Ezekből a párokból épül fel a teljes jelszó, ezért egy 8 karakteres jelszó esetén 4 párunk van, vagyis összesen (15*3*2+15+5+1)^4 = 151 807 041 jelszó lehetséges eddig.

Azonban a párosok egymás mellé kerülnek és így létrejöhet olyan kombináció, ami nem megengedett, pl.: eeee, bbbb, abca, stb., tehát ezeket ki kell vonnunk. Több millió ilyen van, de az egyszerűség kedvéért most hanyagoljuk ezeket és nem vonunk ki semmit, inkább az összes jelszóval számolunk tovább.

Jöhetnek a számok. A magánhangzókat cserélgetjük számokra, de minden jelszóban csak egyet. Egy jelszóban 4 pár van, vagyis maximum 4 magánhangzónk lehet, ezeket az 5 szám egyikére cseréljük, vagyis ((15*3*2+15+5+1)^4)*4*5 eddig.

A jelszavunk mostanra már csak 7 betűt tartalmazhat. Mivel csak egy nagybetű lehet a jelszóban, ezért minden egyes jelszót még hétszer le kell generálnunk, és ezzel meg is vagyunk, összesen ((15*3*2+15+5+1)^4)*4*5*7 = 21 252 985 740 jelszó létezhet. Hát ez nem sok.

Maradjunk a LinkedIn-nél, úgyis onnan indult ki az egész. A jelszavakat SHA-1-el hashelték, salt nélkül. Az én gépem egy Core i5-ös notebook, tehát nem különösebben gyors, ráadásul php-ben írtam a feladatra programot, ami szintén nem gyors. Ezzel másodpercenként kb. 350 000 jelszónak tudom előállítani az SHA-1 hashét, vagyis kb. 16 óra alatt megvan az összes. Valójában persze ennél még rövidebb is lenne az idő, hiszen most beleszámoltam a nem megengedett jelszavakat is.

Persze 10 karakterrel is kiszámoltam, úgy 97 nap alatt lehetne előállítani a hasheket, de ebben is benne vannak a nem megengedett jelszók, szóval ez is jóval rövidebb lenne.

Ez egyáltalán nem sok :) Egy komolyabb géppel, C-ben megírva nem tudom mennyi hasht lehet előállítani, de a 10 millió nem tűnik soknak, és a 97 nap máris lecsökkent 3-ra, és ezt még mindig csak egy átlag gép számolja, nem egy cluster vagy egy szuperszámítógép.

Szóval ez így nem jó. Ahhoz, hogy tényleg nagyon biztonságos legyen az ezzel generált jelszó, nagyon hosszúnak kellene lennie, viszont akkor meg elveszik a kiejthetőség előnye.

A 8 karaketeres jelszo egy mesterseges korlat. A kiejtheto jelszo korlatja olyan 16-20 karakter korul van. Ez egy.

Az i es az o is lehet maganhangzo, ha kikotjuk, hogy az nem lehet a nagybetu. Kis betukent beirva elegge elter a kinezet mindkettonek minden fontban. Ez megint noveli a kombok szamat.

Kikothetunk olyat is, hogy pl. lehet ket nagybetu a jelszoban, de csak szotaghataron, vagyis a KarLapaj az egy ertelmes, de a cHarWay nem (a H a nagy a C helyett).

Mondhatunk tovabba olyat is, hogy egy szotag akkor lehet 3 karakteres, ha egy massalhangzonak minosulo kombinacio jott elo (ch, kh, ph, sh, th) (ugy ertettem, hogy a te algoritmusod ezeket egy szotagnak szamolja). Az algoritmus 3 karaktert general, es legfeljebb az egyiket a vegen eldobja.

Nem sokkal, de megnoveli a kombinaciok szamat.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

További karakter kivételeket jelöltem meg: G 6 B 8

Sajnos nyomtatásban meg kicsi betűknél ezeket sem lehet mindig megkülönböztetni (Win OEM matricánál sem mindig nekem). Meg arra is gondolni akarok, hogy ha egy papírra levésem a user-nek, akkor minél jobban kizárjam a hiba lehetőséget.

A variációk száma így alakul a maradék karakter készlettel (nem kiejthető):

18 * 18 * 5 * (18+18+5) * (18+18+5) * (18+18+5) * (18+18+5) * (18+18+5) vs 256^8

8 char pass / 1.8e+11 vs 1.8e+19
10 char pass / 3.1e+14 vs 1.2e+24

Ugyanez kiejthetőnél (legkevesebb variációs esetet véve - vagyis 2 db magánh és 1 mássalh egymás mellett):

5 * 18 * 3 * 3 * 15 * 3 * 3 * 15 vs 256^8

8 char pass / 1.6e+6 vs 1.8e+19
10 char pass / 1.4e+7 vs 1.2e+24

Viszont az ASCII karakterekből 94 db használható (code 33 - 126), ezért ezzel nézve így alakul:

18 * 18 * 5 * (18+18+5) * (18+18+5) * (18+18+5) * (18+18+5) * (18+18+5) vs 94^8

8 char pass / 1.8e+11 vs 6.0e+15
10 char pass / 7.1e+14 vs 5.3e+19

Ugyanez kiejthetőnél (legkevesebb variációs esetet véve - vagyis 2 db magánh és 1 mássalh egymás mellett):

5 * 18 * 3 * 3 * 15 * 3 * 3 * 15 vs 94^8

8 char pass / 1.6e+6 vs 6.0e+15
10 char pass / 1.4e+7 vs 5.3e+19

Kiváltottam az Array osztály shuffle metódusát saját funkcióval, hogy a kód kompatibilis legyen Ruby 1.8.7 alatti verziókkal is.

"Mindössze annyi volt a gondom vele, hogy bizonyos karaktereket nem akarok hogy megjelenjenek a jelszóban. Olyanokra gondolok, amelyek leírva vagy nyomtatva, betűtípustól függően összekeverhetők. Ilyen a kicsi "i" betű, kicsi "L" betű és nagy "L" betű, "O" betű és nulla stb."

Nálam a pwgen man-ja ír olyat, hogy:

       -B, --ambiguous
              Don't use characters that could be confused  by  the  user  when
              printed,  such  as 'l' and '1', or '0' or 'O'.  This reduces the
              number of possible passwords significantly, and as such  reduces
              the  quality  of  the passwords.  It may be useful for users who
              have bad vision, but in general use of this option is not recom‐
              mended.