Generar contraseña en python

Me gustaría generar algunas contraseñas alfanuméricas en python. Algunas formas posibles son:

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 

Pero no me gustan los dos porque:

  • way 1 solo selecciona caracteres únicos y no puedes generar contraseñas donde length> len (caracteres)
  • La manera 2 tenemos una variable sin usar y no puedo encontrar una buena manera de evitar eso

Entonces, ¿alguna otra buena opción?

PD: Aquí estamos con algunas pruebas con timeit para 100000 iteraciones:

 ''.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 

Entonces, el ganador es ''.join(choice(chars) for _ in xrange(length)) .

Python 3.6 en adelante

Debe usar el módulo de secretos para generar contraseñas seguras criptográficamente, que están disponibles a partir de Python 3.6. Adaptado de la documentación:

 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 

Para la gente crypto-PRNG por ahí:

 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)) 

Tenga en cuenta que para una distribución uniforme, la longitud de la cadena de chars debe ser un divisor integral de 128; De lo contrario, necesitará una forma diferente de elegir uniformemente desde el espacio.

ADVERTENCIA: esta respuesta debe ignorarse debido a problemas de seguridad críticos.

La opción # 2 parece bastante razonable, excepto que podría agregar un par de mejoras:

 ''.join(choice(chars) for _ in range(length)) # in py2k use xrange 

_ es una variable convencional “No me importa lo que está ahí”. Y no necesita una lista de comprensión allí, la expresión del generador funciona bien para str.join . Tampoco está claro qué significa “lento”, si es la única manera correcta .

Creo que esto hará el truco. random.SystemRandom usa la misma función criptográfica random subyacente que os.urandom pero usa la interfaz random familiar. Esta función no estará sujeta a lo raro de 128 bytes como en la respuesta 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) 

Sugiero lo siguiente para aquellos atrapados en 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))]) 

Esto tiene la ventaja sobre la solución de Ben Mosher de que cada símbolo de los símbolos tiene un cambio igual de ocurrencia, mientras que el uso de módulo favorece ligeramente los primeros símbolos en el alfabeto. El alfabeto de los símbolos también es más grande en esta sugerencia.

Escribí un guión con mis preferencias, que en su mayoría están relacionadas con evitar errores al transcribir y recordar. (Por ejemplo: eliminar caracteres un tanto ambiguos y no repetidos).

 import optparse import os import random import sys DEFAULT_CHARS = "234679ADEFGHJKLMNPRTUWabdefghijkmnpqrstuwy" DEFAULT_LEN = 18 def choices(options, length, choice=random.choice): return (choice(options) for _ in xrange(length)) def choices_non_repeated(options, length, choice=random.choice): assert len(options) > 1 last = choice(options) count = 0 while count < length: yield last count += 1 while True: value = choice(options) if value != last: last = value break def main(args): op = optparse.OptionParser(add_help_option=False) op.add_option("--help", action="help", help="show help message and exit") op.add_option("-b", "--bare", action="store_true", default=False, help="print passwords without trailing newline") op.add_option("-c", "--chars", metavar="SET", nargs=1, default=DEFAULT_CHARS, help="character set to use (default: %default)") op.add_option("--repeat", action="store_true", default=False, help="allow repetition") op.add_option("-l", "--len", dest="max", nargs=1, type="int", default=DEFAULT_LEN, help="max length (default: %default)") op.add_option("--min", nargs=1, type="int", default=None, help="min length (defaults to max)") op.add_option("-n", "--count", nargs=1, type="int", default=None, help="number of passwords to generate (default: %default)") op.add_option("--cols", type="int", default=None, help="number of columns to use") opts, args = op.parse_args(args) if args: op.error("unknown arguments") if os.isatty(sys.stdin.fileno()) and ( opts.count is None and opts.cols is None and not opts.bare ): opts.cols = 80 // (opts.max + 1) opts.count = opts.cols * 25 else: if opts.count is None: opts.count = 1 if opts.cols is None: opts.cols = 1 if opts.bare and opts.cols != 1: op.error("bare output requires --cols=1") if opts.min == None: opts.min = opts.max if any(x < 1 for x in [opts.cols, opts.count, opts.min, opts.max]): op.error("values must be >= 1") choices_func = choices_non_repeated if opts.repeat: choices_func = choices elif len(set(opts.chars)) < 2: op.error("must allow repetition or provide a longer character set") return "op.error shouldn't return" col = 0 for _ in xrange(opts.count): length = random.randint(opts.min, opts.max) password = "".join(choices_func(opts.chars, length)) sys.stdout.write(password) if not opts.bare: col += 1 if col == opts.cols: sys.stdout.write("\n") col = 0 else: sys.stdout.write(" ") if __name__ == "__main__": sys.exit(main(sys.argv[1:])) 

Es posible que desee utilizar el map lugar de las listas de comprensión:

 ''.join(map(lambda x: random.choice(chars), range(length)))