Je voudrais générer des mots de passe alphanumériques en python. Certaines manières possibles sont:
import string
from random import sample, choice
chars = string.letters + string.digits
length = 8
''.join(sample(chars,length)) # way 1
''.join([choice(chars) for i in range(length)]) # way 2
Mais je n'aime pas les deux parce que:
i
variable inutilisée et je ne trouve pas le bon moyen d'éviter celaAlors, d'autres bonnes options?
P.S. Nous voici donc avec quelques tests avec timeit
pour 100 000 itérations:
''.join(sample(chars,length)) # way 1; 2.5 seconds
''.join([choice(chars) for i in range(length)]) # way 2; 1.8 seconds (optimizer helps?)
''.join(choice(chars) for _ in range(length)) # way 3; 1.8 seconds
''.join(choice(chars) for _ in xrange(length)) # way 4; 1.73 seconds
''.join(map(lambda x: random.choice(chars), range(length))) # way 5; 2.27 seconds
Ainsi, le gagnant est ''.join(choice(chars) for _ in xrange(length))
.
Vous devez utiliser le module secrets pour générer des mots de passe sûrs sur le plan cryptographique, qui est disponible à partir de Python 3.6. Adapté de la documentation:
import secrets
import string
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(20)) # for a 20-character password
Pour les gens de crypto-PRNG là-bas:
def generate_temp_password(length):
if not isinstance(length, int) or length < 8:
raise ValueError("temp password must have positive length")
chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
from os import urandom
return "".join(chars[ord(c) % len(chars)] for c in urandom(length))
Notez que pour une distribution paire, la longueur de chaîne chars
doit être un diviseur entier de 128; sinon, vous aurez besoin d'une manière différente de choisir uniformément dans l'espace.
AVERTISSEMENT cette réponse doit être ignorée en raison de problèmes de sécurité critiques!
L'option n ° 2 semble tout à fait raisonnable, sauf que vous pourriez ajouter quelques améliorations:
''.join(choice(chars) for _ in range(length)) # in py2k use xrange
_
est une variable conventionnelle "Je me fiche de ce qui s'y trouve". Et vous n'avez pas besoin de compréhension de liste là-bas, l'expression du générateur fonctionne très bien pour str.join
. Il n'est pas clair non plus ce que signifie "lent", si c'est la seule manière correcte .
Je pense que ça fera l'affaire. random.SystemRandom
utilise la même fonction cryptographique aléatoire sous-jacente que os.urandom
mais il utilise l'interface familière random
. Cette fonction ne sera pas soumise à l'étrange chose de 128 octets comme dans la réponse de Ben.
import random
import string
def gen_random_string(char_set, length):
if not hasattr(gen_random_string, "rng"):
gen_random_string.rng = random.SystemRandom() # Create a static variable
return ''.join([ gen_random_string.rng.choice(char_set) for _ in xrange(length) ])
password_charset = string.ascii_letters + string.digits
gen_random_string(password_charset, 32)
Je suggère ce qui suit pour ceux qui sont coincés sur python <3.6:
import os, math, string, struct
def generate_password(pass_len):
symbols = string.printable.strip()
return ''.join([symbols[x * len(symbols) / 256] for x in struct.unpack('%dB' % (pass_len,), os.urandom(pass_len))])
Cela a l'avantage sur la solution de Ben Mosher que chaque symbole des symboles a un changement égal d'occurrence tandis que l'utilisation du module favorise légèrement les premiers symboles de l'alphabet. L'alphabet des symboles est également plus grand dans cette suggestion.