高品质,简单的随机密码生成器

Mik*_* R. 68 python security random passwords

我有兴趣创建一个非常简单,高(加密)质量的随机密码生成器.有一个更好的方法吗?

import os, random, string

length = 13
chars = string.ascii_letters + string.digits + '!@#$%^&*()'
random.seed = (os.urandom(1024))

print ''.join(random.choice(chars) for i in range(length))
Run Code Online (Sandbox Code Playgroud)

Tho*_*nin 46

密码的难点在于使它们足够强大并且仍然能够记住它们.如果密码不是由人类记住的,那么它实际上不是密码.

你使用Python os.urandom():那很好.对于任何实际目的(甚至加密),输出os.urandom()与真正的alea无法区分.然后你用它作为种子random,这不太好:一个是非加密的PRNG,它的输出可能会展示一些不会在统计测量工具中注册的结构,但可能被智能攻击者利用.你应该一直工作os.urandom().为简单起见:选择长度为64的字母表,例如字母(大写和小写),数字和两个额外的标点字符(例如"+"和"/").然后,对于每个密码字符,从中获取一个字节os.urandom(),减少模64的值(这是无偏的,因为64除以256)并将结果用作chars数组中的索引.

使用长度为64的字母表,每个字符可获得6位熵(因为2 6 = 64).因此,对于13个字符,您将获得78位熵.在所有情况下,这并不是最终的强大,但已经非常强大(它可能被预算打败,预算将以数月和数十亿美元计算,而不仅仅是数百万美元).

  • @ChristianBenke:请注意,这是无偏见的,因为我们所讨论的`chars []`数组的长度为64,而256(一个字节的可能值的数量)是64的倍数.如果你使用的数组长度是不是256的精确除数,那么选择将是有偏见的(一些字符将比其他字符更可能). (3认同)
  • 请求一个字节,然后获取字节值,它是一个 0 到 255 范围内的整数。 (2认同)
  • 对于仍然感到困惑的其他人,@ThomasPornin 的解决方案将是例如`chars[ord(os.urandom(1)) % len(chars)]` (2认同)

小智 42

XKCD很好地解释了为什么你认为强密码不是.

http://xkcd.com/936/

对于任何理解信息理论和安全性的人,与那些没有(可能涉及混合案件)的人激怒的争论,我真诚地道歉. - Randall Munroe

如果你不理解这个插图所解释的背后数学,不要尝试编写任何应该加密安全的东西,因为它不会.只需将鼠标放下并远离键盘即可.

  • 请让评论有建设性. (14认同)
  • 第一句话是错误的:随机性*确实*制作"加密强"的密码,而漫画则用*随机,*简单密码与*随机,*简单密码对比.英语单词的熵是字典大小的函数,而不是字长.而不是每个字母4.7位,更像是每个字17位.我更容易为一系列中等长度的根词创建助记符,所以假设我创建了一个包含2048个单词的词典.即使攻击者窃取我的列表,每个*随机*选择的单词仍然会为密码短语添加至少11位熵. (8认同)
  • 对于它的价值,有关于此漫画的[IT安全SE的帖子](http://security.stackexchange.com/questions/6095/xkcd-936-short-complex-password-or-long-dictionary-passphrase)杰夫最近用它作为一个很好的问题的例子. (4认同)
  • @jww作为一个例子,考虑"我____你____多!" 如果你要调查大量的英文文本,你会发现你可以很有可能预测空白中的内容.空白不会丢失太多信息; 换句话说,这些遗失词的熵非常低.你可以在"aspergilli ____ graveness ____ doable"中正确填写空白的概率是多少?(在这种情况下,它们是"chawbacons"和"monogamists".)因为缺失的符号是从大集中随机选择的,所以它们的熵很高. (4认同)
  • @jww 17位是基于130,000个单词的字典中的随机选择(大致是OWL2列表中5到11个字母长的锦标赛合法拼字游戏单词的数量).这只是130k侧模具的熵,可以精确计算.每个字的估计值为1.2比特是基于预测真实英语文本中下一个单词的能力.这只是一个估计,将取决于具体的文字.我的评论试图指出两种情况之间的区别; 一个受损的"行吟诗人"不是*随机的.从洗牌字典中绘制单词是. (2认同)

Ric*_*ich 23

仅供在 2020 年以上遇到此问题的任何人使用。Python 3.6+ 有一个secrets专门用于此目的的模块:

import secrets

password_length = 13
print(secrets.token_urlsafe(password_length))
Run Code Online (Sandbox Code Playgroud)

  • 根据[文档](https://docs.python.org/3/library/secrets.html#secrets.token_urlsafe),生成的密码的密码长度为“password_length * 1.3”。引用文档:“返回一个随机的 URL 安全文本字符串,包含 nbytes 随机字节。文本采用 Base64 编码,因此平均每个字节大约产生 1.3 个字符。” (3认同)

Ros*_*ews 13

就在两天前,Kragen Javier Sitaker在http://lists.canonical.org/pipermail/kragen-hacks/2011-September/000527.html上发布了一个程序来实现这一目标(现在走了 - 试试https://github.com/jesterpm/bin/blob/master/mkpasswd)

生成一个随机的,可记忆的密码:http://xkcd.com/936/

示例运行:

kragen无情:〜/ devel/inexorable-misc $ ./mkpass.py 5 12您的密码是"学习损坏保存的住宅阶段".这相当于一个60位密钥.

假设对MS-Cache哈希进行离线攻击,这是常用的最差密码哈希算法,比简单的MD5差一点,那密码将花费2.5e + 03 CPU年来破解我的廉价Celeron E1200.

目前最常见的密码散列算法是FreeBSD的迭代MD5; 破解这样的哈希需要5.2e + 06 CPU-years.

但是现代GPU可以快速破解大约250倍,因此相同的迭代MD5将落在2e + 04 GPU年.

GPU在2011年的运行成本约为每天1.45美元,因此破解密码的成本约为3美元+ 09美元.

我开始使用以这种方式生成的密码代替9-printable-ASCII字符随机密码,这同样强大.Munroe断言这些密码更容易记忆是正确的.然而,仍然存在一个问题:因为每个字符的熵比较少(大约1.7而不是6.6),密码中存在大量冗余,因此诸如ssh定时信道攻击(Song, Wagner和Tian Herbivore的攻击,我在凌晨一年多的凌晨时分在BagdadCafé中从Bram Cohen那里了解到的,并且键盘录音攻击有更好的机会获取足够的信息以使密码可攻击.

我对草食动物攻击的对策,它与9个字符的密码配合得很好但是我的新密码非常烦人,就是输入字符之间延迟半秒的密码,这样定时通道就不会带来很多关于使用的实际字符.此外,9个字符密码的较低长度固有地为草食动物方法提供了更少的咀嚼信息.

其他可能的对策包括使用Emacs shell模式,它在识别密码提示时会在本地提示您输入密码,然后立即发送整个密码,并从其他地方复制并粘贴密码.

正如您所期望的那样,此密码也需要更长的时间来输入:大约6秒而不是大约3秒.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import random, itertools, os, sys

def main(argv):
    try:
        nwords = int(argv[1])
    except IndexError:
        return usage(argv[0])

    try:
        nbits = int(argv[2])
    except IndexError:
        nbits = 11

    filename = os.path.join(os.environ['HOME'], 'devel', 'wordlist')
    wordlist = read_file(filename, nbits)
    if len(wordlist) != 2**nbits:
        sys.stderr.write("%r contains only %d words, not %d.\n" %
                         (filename, len(wordlist), 2**nbits))
        return 2

    display_password(generate_password(nwords, wordlist), nwords, nbits)
    return 0

def usage(argv0):
    p = sys.stderr.write
    p("Usage: %s nwords [nbits]\n" % argv0)
    p("Generates a password of nwords words, each with nbits bits\n")
    p("of entropy, choosing words from the first entries in\n")
    p("$HOME/devel/wordlist, which should be in the same format as\n")
    p("<http://canonical.org/~kragen/sw/wordlist>, which is a text file\n")
    p("with one word per line, preceded by its frequency, most frequent\n")
    p("words first.\n")
    p("\nRecommended:\n")
    p("    %s 5 12\n" % argv0)
    p("    %s 6\n" % argv0)
    return 1

def read_file(filename, nbits):
    return [line.split()[1] for line in
            itertools.islice(open(filename), 2**nbits)]

def generate_password(nwords, wordlist):
    choice = random.SystemRandom().choice
    return ' '.join(choice(wordlist) for ii in range(nwords))

def display_password(password, nwords, nbits):
    print 'Your password is "%s".' % password
    entropy = nwords * nbits
    print "That's equivalent to a %d-bit key." % entropy
    print

    # My Celeron E1200
    # (<http://ark.intel.com/products/34440/Intel-Celeron-Processor-E1200-(512K-Cache-1_60-GHz-800-MHz-FSB)>)
    # was released on January 20, 2008.  Running it in 32-bit mode,
    # john --test (<http://www.openwall.com/john/>) reports that it
    # can do 7303000 MD5 operations per second, but I’m pretty sure
    # that’s a single-core number (I don’t think John is
    # multithreaded) on a dual-core processor.
    t = years(entropy, 7303000 * 2)
    print "That password would take %.2g CPU-years to crack" % t
    print "on my inexpensive Celeron E1200 from 2008,"
    print "assuming an offline attack on a MS-Cache hash,"
    print "which is the worst password hashing algorithm in common use,"
    print "slightly worse than even simple MD5."
    print

    t = years(entropy, 3539 * 2)
    print "The most common password-hashing algorithm these days is FreeBSD’s"
    print "iterated MD5; cracking such a hash would take %.2g CPU-years." % t
    print

    # (As it happens, my own machines use Drepper’s SHA-2-based
    # hashing algorithm that was developed to replace the one
    # mentioned above; I am assuming that it’s at least as slow as the
    # MD5-crypt.)

    # <https://en.bitcoin.it/wiki/Mining_hardware_comparison> says a
    # Core 2 Duo U7600 can do 1.1 Mhash/s (of Bitcoin) at a 1.2GHz
    # clock with one thread.  The Celeron in my machine that I
    # benchmarked is basically a Core 2 Duo with a smaller cache, so
    # I’m going to assume that it could probably do about 1.5Mhash/s.
    # All common password-hashing algorithms (the ones mentioned
    # above, the others implemented in John, and bcrypt, but not
    # scrypt) use very little memory and, I believe, should scale on
    # GPUs comparably to the SHA-256 used in Bitcoin.

    # The same mining-hardware comparison says a Radeon 5870 card can
    # do 393.46 Mhash/s for US$350.

    print "But a modern GPU can crack about 250 times as fast,"
    print "so that same iterated MD5 would fall in %.1g GPU-years." % (t / 250)
    print

    # Suppose we depreciate the video card by Moore’s law,
    # i.e. halving in value every 18 months.  That's a loss of about
    # 0.13% in value every day; at US$350, that’s about 44¢ per day,
    # or US$160 per GPU-year.  If someone wanted your password as
    # quickly as possible, they could distribute the cracking job
    # across a network of millions of these cards.  The cards
    # additionally use about 200 watts of power, which at 16¢/kWh
    # works out to 77¢ per day.  If we assume an additional 20%
    # overhead, that’s US$1.45/day or US$529/GPU-year.
    cost_per_day = 1.45
    cost_per_crack = cost_per_day * 365 * t
    print "That GPU costs about US$%.2f per day to run in 2011," % cost_per_day
    print "so cracking the password would cost about US$%.1g." % cost_per_crack

def years(entropy, crypts_per_second):
    return float(2**entropy) / crypts_per_second / 86400 / 365.2422

if __name__ == '__main__':
    sys.exit(main(sys.argv))
Run Code Online (Sandbox Code Playgroud)


yos*_*ssi 10

实施@Thomas Pornin解决方案

import M2Crypto
import string

def random_password(length=10):
    chars = string.ascii_uppercase + string.digits + string.ascii_lowercase
    password = ''
    for i in range(length):
        password += chars[ord(M2Crypto.m2.rand_bytes(1)) % len(chars)]
    return password
Run Code Online (Sandbox Code Playgroud)

  • 你可以简单地使用os.urandom(1)(加密强)并从M2Crypto中删除依赖 (4认同)
  • 使用`%len(chars)`这种方式对`chars`中的前8个字符略有偏差.这些字母中的每一个都会出现1.95%的时间,而其他字符则为1.56%. (2认同)

OJW*_*OJW 7

XKCD方法的另一个实现:

#!/usr/bin/env python
import random
import re

# apt-get install wbritish
def randomWords(num, dictionary="/usr/share/dict/british-english"):
  r = random.SystemRandom() # i.e. preferably not pseudo-random
  f = open(dictionary, "r")
  count = 0
  chosen = []
  for i in range(num):
    chosen.append("")
  prog = re.compile("^[a-z]{5,9}$") # reasonable length, no proper nouns
  if(f):
    for word in f:
      if(prog.match(word)):
        for i in range(num): # generate all words in one pass thru file
          if(r.randint(0,count) == 0): 
            chosen[i] = word.strip()
        count += 1
  return(chosen)

def genPassword(num=4):
  return(" ".join(randomWords(num)))

if(__name__ == "__main__"):
  print genPassword()
Run Code Online (Sandbox Code Playgroud)

样本输出:

$ ./randompassword.py
affluent afford scarlets twines
$ ./randompassword.py
speedboat ellipse further staffer
Run Code Online (Sandbox Code Playgroud)


rsa*_*saw 7

我知道这个问题是在2011年发布的,但是对于那些现在在2014年及以后的人来说,我有一件事要说:抵制重建轮子的危险.

在这些情况下,最好的办法是搜索开源软件,例如,将搜索限制为github结果.到目前为止,我发现了最好的东西:

https://github.com/redacted/XKCD-password-generator


Win*_*ert 5

生成密码时,您不能信任 python 的伪随机数生成器。它不一定是加密随机的。您正在播种伪随机数生成器,os.urandom这是一个良好的开端。但是在那之后你依赖于python的生成器。

更好的选择是random.SystemRandom()从与urandom. 根据python文档,应该足以用于加密使用。该SystemRandom班给你的一切,主随机类做,但你并不需要对伪随机性担心。

使用 random.SystemRandom 的示例代码(适用于 Python 3):

import random, string
length = 13
chars = string.ascii_letters + string.digits + '!@#$%^&*()'

rnd = random.SystemRandom()
print(''.join(rnd.choice(chars) for i in range(length)))
Run Code Online (Sandbox Code Playgroud)

注意:您的里程可能会有所不同 - Python 文档说 random.SystemRandom 可用性因操作系统而异。

  • 我认为你在这里混淆了你的概念。在确定性计算机中,没有真正的随机性;除非你有专门的硬件,否则一切(包括`/dev/urandom`)都是伪随机的。 (5认同)

mvr*_*rak 1

这样行得通。完全没问题。如果您有其他规则,例如排除字典单词,那么您可能也想包含这些过滤器,但使用该设置随机生成字典单词的可能性非常小。