在python中生成密码

我想在python中生成一些字母数字密码。 一些可能的方法是:

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

但我不喜欢这两者,因为:

  • 方法1仅选择唯一字符,并且您不能生成长度> len(字符)的密码
  • 方式2我们有''.join(choice(chars) for _ in xrange(length))变量未使用,我找不到很好的方法来避免这种情况

那么,还有其他好的选择吗?

附言 因此,我们在这里进行了''.join(choice(chars) for _ in xrange(length))的100000次迭代测试:

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

因此,获胜者是''.join(choice(chars) for _ in xrange(length))

HardQuestions asked 2020-06-29T17:19:58Z
7个解决方案
89 votes

从Python 3.6开始

您应该使用secrets模块来生成密码安全的密码,该密码可从Python 3.6开始使用。 改编自文档:

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
gerrit answered 2020-06-29T17:20:12Z
33 votes

对于在那里的加密PRNG人士:

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

请注意,对于均匀分布,chars的字符串长度应为128的整数除数; 否则,您将需要一种不同的方法来从空间中统一选择。

Ben Mosher answered 2020-06-29T17:20:37Z
13 votes

警告由于严重的安全问题,应忽略此答案!

选项2似乎很合理,除了您可以添加一些改进:

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

_是常规的“我不在乎里面有什么”变量。 而且您不需要列表理解,生成器表达式对于str.join来说也很好。如果唯一的正确方法,还不清楚“慢”是什么意思。

SilentGhost answered 2020-06-29T17:21:07Z
5 votes

我认为这可以解决问题。 random.SystemRandom使用与os.urandom相同的底层加密随机函数,但使用熟悉的random接口。 这个函数不会像Ben的答案那样受制于128字节的怪异事物。

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)
Tim Ludwinski answered 2020-06-29T17:21:27Z
4 votes

对于那些卡在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))])

与本·莫舍(Ben Mosher)的解决方案相比,它的优势在于,符号中的每个符号发生的变化均等,而使用模数则稍微有利于字母中的第一个符号。 在此建议中,符号字母也较大。

gardarh answered 2020-06-29T17:21:52Z
-4 votes

我写了自己喜好的脚本,主要是为了避免抄写和记忆时出错。 (例如:删除有些模棱两可且没有重复的字符。)

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:]))
answered 2020-06-29T17:22:12Z
-4 votes

您可能要使用map而不是列表推导:

''.join(map(lambda x: random.choice(chars), range(length)))
Sergey Stolyarov answered 2020-06-29T17:22:32Z
translate from https://stackoverflow.com:/questions/3854692/generate-password-in-python