Kennworterzeugung anpassen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Marek06
User
Beiträge: 13
Registriert: Dienstag 17. April 2012, 08:51

Hallo,

meine Kenntnisse in Python sind nicht so gut, weshalb ich hier um Hilfe bitte.

Ursprünglicher Quelltext:

Code: Alles auswählen

def createInitialPwd(col,key,config):
    flag = config.get(ldapConst.PWD_FLAG,0)
    if flag:
        param=config[ldapConst.FCTS]
        length=utilGeneral.isInt(param.get(ldapConst.PWDLENGTH,8))
        char_sets=[string.lowercase, string.uppercase, string.digits, string.punctuation]
        chars_up_lo=string.lowercase+string.uppercase+string.digits
        length -= 1
        chars=[]
        for s in char_sets:
        	chars.append(random.choice(s))
        while len(chars) < length:
        	chars.append(random.choice(''.join(char_sets)))
        random.shuffle(chars)
        s=''.join(chars)
        y=random.choice(chars_up_lo)
        config[ldapConst.PWD_TMP]=y + s
        return y + s
    return ''
Ich habe aber die Anforderung, dass im Kennwort mindestens 1 Großbuchstabe, 1 Kleinbuchstabe, 1 Zahl, 1 Sonderzeichen enthalten sein muss. Dies geht aber oben nicht immer, da dies ja Zufallswerte sind.

Dazu hatte ich jetzt die folgende Idee:

1. Erzeuge ein 8-stelliges Kennwort in Kleinbuchstaben (funktioniert)
2. Ersetze 1-2 Zeichen mit Zahlen (funktioniert)
3. Ersetze 1-2 Zeichen mit Großbuchstaben (funktioniert)
4. Ersetze 1-2 Zeichen mit Sonderzeichen (funktioniert nicht - wie kann ich das umsetzen bzw. was ist falsch)
5. Durchmischen der Ziffern (funktioniert)

Quellcode dazu:

Code: Alles auswählen

def createInitialPwd(col,key,config):
    flag = config.get(ldapConst.PWD_FLAG,0)
    if flag:
        param=config[ldapConst.FCTS]
        length=utilGeneral.isInt(param.get(ldapConst.PWDLENGTH,8))
        char_sets=string.lowercase
        char_sets_zeichen=string.punctuation
        chars_up_lo=string.lowercase+string.uppercase+string.digits
        length -= 1
        chars=""
        for i in range(length):
        	next_index=random.randrange(len(char_sets))
        	chars = chars + char_sets[next_index]
        # replace 1 or 2 characters with a number - works
        for i in range(random.randrange(1,3)):
        	replace_index = random.randrange(len(chars)//2)
        	chars = chars[0:replace_index] + str(random.randrange(10)) + chars[replace_index+1:]
        # replace 1 or 2 letters with an uppercase letter - works
	for i in range(random.randrange(1,3)):
		    replace_index = random.randrange(len(chars)//2,len(chars))
		    chars = chars[0:replace_index] + chars[replace_index].upper() + chars[replace_index+1:]
       # Ersetzen der Sonderzeichen funktioniert nicht!!
        random.shuffle(chars)
        s=''.join(chars)
        y=random.choice(chars_up_lo)
        config[ldapConst.PWD_TMP]=y + s
        return y + s
    return ''
Schon mal schönen dank im Voraus. 8)
Gruss M.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich würde da anders heran gehen und einfach eine Menge aus Zeichen aus verschiedenen "Zeichenpools" (Großbuchstaben, Sonderzeichen, ...) für ein Passwort zusammenstellen, welche ich anschließend nur noch mischen würde. Damit kannst Du solche Regeln total flexibel handhaben, da Du einfach für jeden Zeichenpool definierst, wie viele daraus in das Passwort einfließen sollen.

Code: Alles auswählen

char_sources = {
    string.ascii_uppercase: 5,
    string.ascii_lowercase: 3,
    "!\"§$%&/()=?": 2
    ...
}
# für jeden Key, Value * beliebiges Zeichen nehmen (random.choice)
# am Schluss die Sequenz einmal "shufflen"
Ach ja, man könnte auch einfach in diesen Thread gucken... :twisted: Da steht ja der Ansatz sowie u.a. auch eine Lösung für die Frage drin...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Marek06
User
Beiträge: 13
Registriert: Dienstag 17. April 2012, 08:51

Dies hatte ich ja auch so umgesetzt (Trotzdem danke für den Hinweis) 8) . Aber wenn ich die folgende Funktion richtig verstanden habe, dann wird jeweils irgendein Zeichen aus dem String eingesetzt und somit kann es sein, dass z.B. ein Sonderzeichen nicht mit ausgegeben wird (dies würde dann zum Fehler führen).
...
char_sets=[string.lowercase, string.uppercase, string.digits, string.punctuation]
...
for s in char_sets:
chars.append(random.choice(s))
while len(chars) < length:
chars.append(random.choice(''.join(char_sets)))
...
Oder habe ich das jetzt falsch verstanden???
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Es gibt nicht *den* String, sondern eine Menge von Strings. Und aus jeder Menge (ich habe das oben Zeichenpool genannt), nimmt der Ansatz von EyDu genau *ein* Zeichen! Anschließend füllt er den Rest des Passworts mit *beliebigen* Zeichen aus der Vereinigung alles Pools auf.

Das kann man doch einfach per Hand in einer Shell testen - und das Handtieren mit Listen und Sequenzen allgemein ist halt Grundlagenwissen:

Code: Alles auswählen

char_sets=[string.ascii_lowercase, string.ascii_uppercase, string.digits, string.punctuation]
char_sets
> ['abcdefghijklmnopqrstuvwxyz',
 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
 '0123456789',
 '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~']

''.join(char_sets)
> 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
Meine Idee oben war es, in einem Dictionary eben zusätzlich zum Zeichenpool auch die Menge an Zeichen zu hinterlegen, die in das Passwort einfließen müssen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Hyperion hat geschrieben:Meine Idee oben war es, in einem Dictionary eben zusätzlich zum Zeichenpool auch die Menge an Zeichen zu hinterlegen, die in das Passwort einfließen müssen.
Wobei man die Anzahl der Zeichen nicht festlegen sollte, weil man damit die Moeglichkeiten unnoetig einschraenkt.
Ich wuerde stattdessen ein Intervall festlegen und die Anzahl daraus zufaellig waehlen und zum Schluss mit einer Zeichenmenge zur Wunschlaenge auffuellen.
Marek06
User
Beiträge: 13
Registriert: Dienstag 17. April 2012, 08:51

Okay das mit der variablen Wunschlänge ist dort jetzt noch nicht mit programmiert. Aber zur zeit habe ich es wie folgt gelöst:

Code: Alles auswählen

...
 if flag:
        param=config[ldapConst.FCTS]
        length_lower=utilGeneral.isInt(param.get(ldapConst.PWDLENGTH,2))
        length_upper=utilGeneral.isInt(param.get(ldapConst.PWDLENGTH,2))
        length_digits=utilGeneral.isInt(param.get(ldapConst.PWDLENGTH,2))
        length_punctuation=utilGeneral.isInt(param.get(ldapConst.PWDLENGTH,1))
        char_sets_lower=[string.lowercase]
        char_sets_upper=[string.uppercase]
        char_sets_digits=[string.digits]
        char_sets_punctuation=[string.punctuation]
        chars_up_lo=string.lowercase+string.uppercase+string.digits
        
        # lowercase
        chars_lower=[]
        for s in char_sets_lower:
        	chars_lower.append(random.choice(s))
        while len(chars_lower) < length_lower:
        	chars_lower.append(random.choice(''.join(char_sets_lower)))
        s=''.join(chars_lower)
      	
      	# uppercase
        chars_upper=[]
        for t in char_sets_upper:
        	chars_upper.append(random.choice(t))
        while len(chars_upper) < length_upper:
        	chars_upper.append(random.choice(''.join(char_sets_upper)))
        t=''.join(chars_upper)
        
        # digits
        chars_digits=[]
        for u in char_sets_digits:
        	chars_digits.append(random.choice(u))
        while len(chars_digits) < length_digits:
        	chars_digits.append(random.choice(''.join(char_sets_digits)))
        u=''.join(chars_digits)
        
        # punctuation
        chars_punctuation=[]
        for v in char_sets_punctuation:
        	chars_punctuation.append(random.choice(v))
        while len(chars_punctuation) < length_punctuation:
        	chars_punctuation.append(random.choice(''.join(char_sets_punctuation)))
        v=''.join(chars_punctuation)
        
        # mixed
        chars=s + t + u + v
        a = ''.join(random.sample(chars, len(chars)))
        
        y=random.choice(chars_up_lo)
        config[ldapConst.PWD_TMP]=y + a
        return y + a
...
Marek06
User
Beiträge: 13
Registriert: Dienstag 17. April 2012, 08:51

Ergänzung: Der Block für "mixed" muss noch angepasst werden:

Code: Alles auswählen

...
 # mixed
        chars=[s, t, u, v]
        random.shuffle(chars)
        a=''.join(chars)
...
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Jetzt fällt dir sicher noch auf, dass dein Code einige Wiederholungen hat die sich nur minimal unterscheiden. Das kann und sollte man zusammenfassen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Und die Code-Blöcke sind dazu noch scheußlich umständlich! Du erzeugst oben erst mal Listen von Sequenzen, um erstere dann in einer sinnlosen ``for``-Schleife auf ein Element zu reduzieren, welches Du obendrein noch sinnfrei per Zufall auswählst...

Code: Alles auswählen

for s in [string.ascii_lowercase]:
    print(s)

> abcdefghijklmnopqrstuvwxyz
Das kann man sich komplett sparen!

Du kannst Dir doch zufällige Sequenzen einer bestimmten Länge locker flockig mit einem Generator-Ausdruck erstellen:

Code: Alles auswählen

''.join(random.choice(string.ascii_lowercase) for _ in range(4))
'zbpp'
Wenn Du jetzt noch meinen Tipp von oben her nimmst und die Zeichenpools inklusive der Länge in *einer* Datenstruktur (z.B. Liste von Listen, Dictionary, o.ä.) zusammenfasst, kannst Du um diesen Ausdruck eine Schleife basteln und in ihm die feste Zeichenkette und die Anzahl gegen die dynamischen Parameter austauschen und schon hast Du das alles in vier Zeilen gelöst.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@Marek06: die for-Schleifen über einelementige Listen sind ziemlich umständlich, ebenso die »join«s zwei Zeilen tiefer. Und warum Du dann den ersten Durchgang aus der »while«-Schleife herausgezogen hast? Und wer ist dieser util-General und was macht der in einem Python-Skript? (Kommt der von der NSA?)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Sirius3 hat geschrieben:Und wer ist dieser util-General und was macht der in einem Python-Skript? (Kommt der von der NSA?)
Es scheint etwas mit LDAP zu tun zu haben... die Typprüfung empfinde ich zwar als scheußlich, aber wer weiß schon wie das API da tatsächlich zu handlen ist...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

LDAP? Den Geheimdienst kannte ich noch gar nicht. :-)

Scherz beiseite: Ich denke worauf Sirius hinaus wollte ist dass das eigentlich nichts mit der Erzeugung eines Passworts zu tun hat und damit nicht in die Funktion gehört. Oder praktischer ausgedrückt, um die Funktion zu testen sollte man nicht zwingend LDAP benötigen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:LDAP? Den Geheimdienst kannte ich noch gar nicht. :-)
Vielleicht arbeiten die mit BlitzDings-Geräten... :mrgreen:
BlackJack hat geschrieben: Scherz beiseite: Ich denke worauf Sirius hinaus wollte ist dass das eigentlich nichts mit der Erzeugung eines Passworts zu tun hat und damit nicht in die Funktion gehört. Oder praktischer ausgedrückt, um die Funktion zu testen sollte man nicht zwingend LDAP benötigen.
Ach so, ja klar... aber ich vermute der OP hat keine Funktionen, sondern arbeitet nur auf Modulebene. Damit hat sich das dann eh erledigt :twisted:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Marek06
User
Beiträge: 13
Registriert: Dienstag 17. April 2012, 08:51

Hallo,

nochmals danke für die Hilfe (wie gesagt meine Kenntnisse in Python sind nicht so gut) und der Code
wurde so von meinem Vorgänger geschrieben. 8)

Zum Thema utilGeneral (und ja es ist LDAP):
lt. unserer Beschreibung ist dies ein Modul zur allgemeinen Nutzung (printTime, createLogger, makeDirs, ...)

Ich habe es jetzt weiter eingedampft (lt. der hilfreichen Tipps):

Code: Alles auswählen

if flag:
        param=config[ldapConst.FCTS]
        chars_up_lo=string.lowercase+string.uppercase+string.digits
        zahl=[1, 2, 3, 4]
        x=random.sample(zahl, 4) 
        for i in x:
        	if (i == 1):
        		s=''.join(random.choice(string.uppercase) for _ in range(i))
        	if (i == 2):
        		t=''.join(random.choice(string.digits) for _ in range(i))
        	if (i == 3):
        		u=''.join(random.choice(string.lowercase) for _ in range(i))
        	if (i == 4):
        		zahl_ind = 1
        		v=''.join(random.choice(string.punctuation) for _ in range(zahl_ind))       

        chars=[s, t, u, v]
        random.shuffle(chars)
        a=''.join(chars)
        
        y=random.choice(chars_up_lo)
        config[ldapConst.PWD_TMP]=y + a
        return y + a
Gruß M.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

OMG... Dir ist hoffentlich klar, was Du da machst? (Ich vermute mal "nein", denn da stehen wieder einmal vollkommen obskure Dinge drin!)

@Zeile 5: Beschreibe doch mal, was Du damit erreichen willst! Kleiner Tipp: ich würde es einfach so formulieren ``shuffle(zahl)``
Dazu kommt der unsinnige Bezeichner ``zahl`` - das sind doch *ZAHLEN*! Dazu ist nicht klar, was diese überhaupt ausdrücken (Indizes?).

@Schleife: Was glaubst Du passiert darin? Wozu überhaupt eine Schleife, wenn Du tatsächlich alles einzeln darin erledigst. Eine Schleife wäre dann sinnvoll, wenn Du die Zeichenpools *und* die dazugehörigen Anzahlen in eine Struktur packst, die man in der Schleife eben *ohne* Fallunterscheidung abarbeitet. Ist Dir klar, dass der Zufall (Zeile 5) der Indizes im Grunde keine Rolle spielt?

@Zeile 17-19: Was genau erhoffst Du Dir darin zu erreichen?

@Zeile 21: Dir ist schon klar, dass da genau *ein* einzelnes Zeichen ausgewählt wird? Und das wird dann in Zeile 23 auch immer hinten an das bisherige Passwort angehängt... (und in Zeile 22 zusätzlich in einer internen Datenstruktur geändert - schlecht, weil man jetzt eine Redundanz drin hat).

Alles in allem: Du wirst auf diese Art nicht weiterkommen! Ohne die Grundlagen wirst Du kein noch so kleines Python-Problem erfolgreich lösen können.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Marek06
User
Beiträge: 13
Registriert: Dienstag 17. April 2012, 08:51

Okay, ich schaue mir die Grundlagen dazu nochmal an. :)
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@Marek06: Zum utilGeneral: Die von Dir angeführten Funktionen sehen so aus, als ob sie sehr einfach durch irgendetwas aus der Standardbibliothek zu ersetzen wären. Warum also eine unnötige und komplizierte Abhängigkeit schaffen???
Kannst Du mir den Sinn der »for«-Schleife erklären? Du würfelst die Reihenfolge, wann s, t, u und v definiert werden.
Dadurch baust Du Dir neben der Wiederholung fast identischem Codes zusätzlich noch eine »if«-Kaskade ein, die nichtmal eine ist, weil Du statt »elif« tatsächlich jedesmal »if« nimmst. Übrigens, Klammern um Bedingungen wo sie nicht gebraucht werden, solltest Du löschen.
Dein Ziel sollte es sein mit nur einem[/b| »random.choice« auszukommen, weil die Zeichenmengen mit ihrer Häufigkeit in einer Liste gespeichert wird, die dann von einer for-Schleife abgearbeitet wird.
Das Vorhandensein von »flag«, »param« und »config« erschließt sich mir in einer Funktion zum Erzeugen eines Passworts nicht.
BlackJack

@Marek06: Das ziemlicher Unsinn was da gemacht wird. Wozu soll `x` und die Schleife gut sein? Das hätte man sich komplett sparen können. `zahl`, `x`, und die Schleife haben keinen Einfluss auf das Ergebnis.

`param` wird nirgends benutzt.

Was soll `chars_up_lo` bedeuten? Wenn ich richtig geraten habe passt der Name nicht zum Wert. `zahl` passt ebenfalls nicht, denn das ist keine Zahl. `zahl_ind` ist zumindest fragwürdig. Die Namen `a` und `y` gehen ja mal gar nicht.

`config` ändern *und* den Wert zurück geben ist irgendwie redundant, die Funktion zum generieren eines Passwortes sollte auch wirklich nur das tun: Ein Passwort generieren.

Deinen Quelltext kann man auf das hier eindampfen:

Code: Alles auswählen

    if flag:
        password_characters = [
            ''.join(random.choice(chars) for _ in range(length))
            for chars, length in [
                (string.uppercase, 1),
                (string.digits, 2),
                (string.lowercase, 3),
                (string.punctuation, 1),
            ]
        ]
        random.shuffle(password_characters)
        return (
            random.choice(string.lowercase + string.uppercase + string.digits)
            + ''.join(password_characters)
        )
Allerdings ist das Passwort nicht so gut wie es sein könnte, denn die einzelnen „Klassen” von Zeichen werden als komplette Gruppe ”gemischt”. Sinvoller wäre es am Ende alle Zeichen zu mischen, damit nicht immer alle Ziffern, alle Grossbuchstaben, usw. zusammen stehen.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Marek06:
Überhaupt wären die Passwörter stärker, wenn Du keine Bedingungen an das Zeichenvorkommen stellst sondern einfach den Zeichenvorrat und die Länge hinreichend groß wählst. Z.B. führte bei der Enigma die Festlegung für das Steckerbrett - dass kein Zeichen auf sich selbst verweisen darf - zu einer Reduktion des Suchraumes um mehrere 10er Potenzen.
Ist der Zeichenvorrat groß, sind triviale Passwörter eh unwahrscheinlich, wenn Du sie (gut randomisiert) generierst.
Marek06
User
Beiträge: 13
Registriert: Dienstag 17. April 2012, 08:51

Nochmals danke für die Tipps (wäre ich sonst nicht so schnell drauf gekommen) :D :D :D

Das Passwort hat bestimmte Voraussetzungen die erfüllt sein müssen (z. B. nicht länger als 8 Zeichen, ... s.o)

Ich habe den Vorschlag aufgenommen und habe es entsprechend angepasst (bestimmt könnte es immer noch besser sein, aber es funktioniert - 8) - mit Grundlagen wäre das bestimmt besser gegangen). Zum Durchmischen der Buchstaben habe ich jetzt den String in eine Liste übertragen und dann erneut ein shuffle drauf gemacht, womit dann auch nicht die ganzen Blöcke getauscht werden.

Code: Alles auswählen

if flag:
        number=[1, 2, 3, 1]
        random.shuffle(number) 
       	password_characters = [
            ''.join(random.choice(chars) for _ in range(length))
            for chars, length in [
                (string.uppercase, number[0]),
                (string.digits, number[1]),
                (string.lowercase, number[2]),
                (string.punctuation, number[3]),
            ]
        ]
        password_characters_tmp=''.join(password_characters)
        password_characters=[]
        for c in password_characters_tmp: password_characters.append(c)
        random.shuffle(password_characters)

        return (
            random.choice(string.lowercase + string.uppercase + string.digits) + ''.join(password_characters)
        )
    return ''
Antworten