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


75

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

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

6
这些密码是用于人还是机器?
cheeken 2011年

21
@JarrodRoberson指出“随机性!=密码学”本身就是可怕的,因为现代密码学是建立在随机性基础上的。并非所有的随机性都是一样的(例如:从“密码”和“密码”中随机选择的密码显然是不安全的),但从本质上讲,密码学==随机性。
NullUserException 2011年

4
这不是random.seed方法,因此random.seed = 'random_string'基本上会破坏该方法并且什么都不做?你的意思是random.seed('random_string')
Nick T

1
os.urandom(1024)获取1024个字节。对我来说似乎有点过分了。播种16或32字节的种子也许更合适。
jww 2014年

1
@nealmcb,如果您发现它很有用-如问题中所述,我的目标是“随机密码生成器”。就我而言,它用于Flask的令牌加密(JWT)以访问Web API,因此,我唯一关心的是它是随机的。并以预定义的长度。因此,我只在寻找一种技术解决方案,而不是在讨论什么是好的密码/短语。是的,我正为此目的使用类似KeePass的解决方案。我认为这些都是关键,不值得记住。有时不知道密码是比较安全的选择。
艾伦·西雅克

Answers:


49

使用密码的困难之处在于使密码足够坚固,并且仍然能够记住它们。如果密码不是人类要记住的密码,那么它实际上并不是密码。

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

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


请稍等...这听起来像是一个愚蠢的问题,但os.urandom()返回type bytes,那么如何减少该mod 64?
肖恩·伯恩

2
要求一个字节,然后获取字节值,该值是0到255之间的整数。
Thomas Pornin 2014年

1
但是,很难记住13个随机字符。这实际上是兰德尔在著名的XKCD漫画中的观点。我建议github.com/redacted/XKCD-password-generator使用它的--acrostic选项来获得一个您更容易记住的模式。
nealmcb 2014年

2
对于仍然感到困惑的其他人,@ ThomasPornin的解决方案例如是chars[ord(os.urandom(1)) % len(chars)]
Christian Benke

3
@ChristianBenke:请注意,这是无偏的,因为chars[]我们正在讨论的数组的长度为64,而256(一个字节的可能值的数量)是64的倍数。 256则选择将有偏差(某些字符比其他字符更有可能)。
Thomas Pornin 2015年

46

XKCD有为什么很好的解释你的想法是强密码都没有

http://xkcd.com/936/

对于那些了解信息理论和安全性并且与不认识信息的人(可能涉及混合案例)激怒的人,我深表歉意。-兰德尔·芒罗

而且,如果您不了解该插图背后数学含义,请不要尝试编写任何应采用密码保护的内容,因为那样就不会。只需将鼠标放下并离开键盘即可。


4
值得一提的是,在IT Security SE上一篇关于该漫画的帖子,Jeff最近将其用作一个很好的问题的例子。
流行

14
请让评论保持建设性。
Tim Post

8
第一句话是错误的:随机性确实会产生“密码学上很强”的密码,而漫画则将非随机,困难的密码与随机,容易的密码短语进行了对比。英语单词的熵是字典大小的函数,而不是单词长度的函数。而不是每个字母4.7位,而是每个单词17位。对我来说,为一系列中等长度的词根创建助记符比较容易,因此假设我创建了一个2048个此类词的字典。即使攻击者窃取了我的列表,每个随机选择的单词仍会在密码短语中至少增加11位熵。
erickson

2
@jww 17位基于130,000个单词的字典中的随机选择(大约在OWL2列表中,锦标赛法律拼字游戏单词的数量介于5至11个字母之间)。这只是一个13万面骰子的熵,可以精确计算出来。每个单词估计1.2位是基于预测真实英文文本中下一个单词的能力。那只是一个估计,将取决于特定的文本。我的评论是要指出这两种情况之间的区别。杂乱无章的“杂种”不是随机的。从改组的字典中提取单词是。
erickson

4
@jww例如,考虑“我____您____很多!” 如果要调查大量的英文文本,则会发现您很有可能预测空白处的内容。空白不会丢失太多信息;换句话说,那些遗漏单词的熵很低。您可以正确填写“ aspergilli ____严重性____可行”中的空白的概率是多少?(在这种情况下,它们是“ chawbacons”和“一夫一妻”。)由于缺少的符号是从一大组中随机选择的,因此它们的熵很高。
erickson 2014年

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 at inexorable:〜/ devel / inexorable-misc $ ./mkpass.py 5 12您的密码是“学习了保存受损的居住阶段”。这相当于一个60位密钥。

从2008年开始,假设我对廉价的Celeron E1200进行脱机攻击,那么该密码将花费2.5e + 03 CPU年,这是对MS-Cache哈希的离线攻击,这是最常用的密码哈希算法,甚至比简单的MD5还要差。

如今,最常见的密码哈希算法是FreeBSD的迭代MD5。破解此类哈希将需要5.2e + 06 CPU年。

但是现代GPU的破解速度大约是其250倍,因此相同的迭代MD5将在2e + 04 GPU年内出现。

该GPU在2011年每天要花费约1.45美元,因此破解密码需要花费3e + 09美元。

我已经开始使用以这种方式生成的密码来代替同样强度很强的9可打印ASCII字符随机密码。Munroe认为这些密码更容易记忆的说法是正确的。但是,仍然存在一个问题:因为每个字符的熵位数要少得多(大约是1.7,而不是6.6),所以密码中存在很多冗余,因此,诸如ssh定时通道攻击(Song, Wagner和Tian Herbivore攻击,这是我几年前一个凌晨从Bagdad咖啡馆的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))

1
您可以在答案的最前面generate_password()加上random.SystemRandom())吗?它可能会帮助登陆这里的人搜索:“ python password generator”
jfs 2015年

11

实施@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

4
您可以简单地使用os.urandom(1)(加密强度高)并从M2Crypto中删除依赖项
Riccardo Galli

2
使用% len(chars)这种方法对中的前8个字符有一些偏见chars。这些字母中的每个字母的出现时间为1.95%,而其他字符为1.56%。
尼克·弗罗斯特

8

我知道这个问题是在2011年发布的,但是对于那些在2014年及以后出现的问题,我有话要说:抵制重新振作的冲动。

在这些情况下,最好的选择是搜索开源软件,例如,将搜索限制为github结果。到目前为止,我发现的最好的事情是:

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


同意 github版本中有一些不错的选项,例如打印出密码具有多少熵,并让您提供“ acrostic”,以便每个单词的第一个字符与您选择的单词匹配。
nealmcb 2014年

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

样本输出:

$ ./randompassword.py
affluent afford scarlets twines
$ ./randompassword.py
speedboat ellipse further staffer

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

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


1
Linux PRNG(/dev/urandom)被认为是加密安全的。请参阅security.stackexchange.com/questions/3936/…
机械蜗牛

5
我认为您在这里混淆了您的概念。在确定性计算机中,没有真正的随机性。/dev/urandom除非您具有专门的硬件,否则所有(包括)都是伪随机的。
NullUserException 2011年

@NullUserExceptionఠ_ఠ+1消费者可以在漆黑的环境中使用网络摄像头或CCD进行此操作(请参阅参考资料LavaRnd)。

@NullUserExceptionఠ_ఠ,您的计算机确实收集了一些随机性来源。但是,我错了,urandom将其与伪随机数生成器混合使用。不过,使用SystemRandom是一个好主意,因为您可以确定它的加密方式是随机的,而python随机数生成器并非一定如此。
Winston Ewert

4

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

import secrets

password_length = 13
print(secrets.token_urlsafe(password_length))

3

考虑到您的评论,

我只需要能够生成比我脑子里想出的密码更安全的密码即可。

似乎您想使用程序来生成密码,而不是仅仅作为练习来编写密码。最好使用现有的实现,因为如果您犯了一个错误,则输出可能会受到影响。了解有关随机数生成器攻击的信息;特别是Debian中的一个著名的RNG错误暴露了人们的SSL私钥。

因此,请考虑使用pwgen。它提供了多个选项,您应该根据计划使用密码的方式来选择这些选项。


1
Debian漏洞是一个实现错误。通常,/dev/urando/dev/random与真正的随机性没有区别(系统启动后的前10分钟左右除外)
Jacco

2

这很容易 :)

def codegenerator():
    alphabet = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    pw_length = 8
    mypw = ""

    for i in range(pw_length):
        next_index = random.randrange(len(alphabet))
        mypw = mypw + alphabet[next_index]
    return mypw

和做:

print codegenerator()

谢谢http://xkcd.com/936/


2
import random


r = random.SystemRandom()


def generate_password(words, top=2000, k=4, numbers=None, characters=None,
                      first_upper=True):
    """Return a random password based on a sorted word list."""
    elements = r.sample(words[:top], k)

    if numbers:
        elements.insert(r.randint(1, len(elements)), r.choice(numbers))
    if characters:
        elements.insert(r.randint(1, len(elements)), r.choice(characters))
    if first_upper:
        elements[0] = elements[0].title()

    return ''.join(elements)


if __name__ == '__main__':
    with open('./google-10000-english-usa.txt') as f:
        words = [w.strip() for w in f]
    print(generate_password(words, numbers='0123456789', characters='!@#$%'))
  • 生成您可以记住的密码
  • 用途 os.urandom()
  • 处理现实世界中的规则,例如加数字,大写字母,字符。

当然可以改进,但这是我使用的。


2

令人鼓舞的@Thomas Pornin解决方案(无法评论@Yossi不精确的答案):

import string, os
chars = string.ascii_letters + string.digits + '+/'
assert 256 % len(chars) == 0  # non-biased later modulo
PWD_LEN = 16
print(''.join(chars[c % len(chars)] for c in os.urandom(PWD_LEN)))

感谢Stephan Lukits的python3更新


在python 3.8中,需要使用'string.ascii_letters',并且对字节进行迭代已经给出了整数,因此'orc(c)'将失败,而'c'将起作用。
斯蒂芬·卢基斯

1

这样就行了。很好。如果您有其他规则,例如排除字典单词,那么您可能还希望包括这些过滤器,但是使用该设置随机生成字典单词的可能性非常小。


1

您的实现存在一些问题:

random.seed = (os.urandom(1024))

这不会为随机数生成器提供种子。它将seed函数替换为字节串。您需要致电seed,例如random.seed(…)

print ''.join(random.choice(chars) for i in range(length))

Python的默认PRNG是Mersenne Twister,它不是具有加密功能的PRNG,因此我对将其用于加密目的非常谨慎。该random模块包括random.SystemRandom,至少在大多数* nix系统上,应使用CSPRNG。但是

random.choice(chars)

……实现为……

def choice(self, seq):
    """Choose a random element from a non-empty sequence."""
    return seq[int(self.random() * len(seq))]  # raises IndexError if seq is empty

…在Python 2中。不幸的是,self.random这是一个C函数,因此很难看到它。这里的代码味道是,该代码几乎肯定不会统一选择。该代码在Python 3中已完全更改,并且在确保一致性方面做得更好。需要randrange注意的Python 3文档,

在版本3.2中进行了更改:randrange()在产生均等分布的值方面更为复杂。以前它使用的样式int(random()*n)可能会产生稍微不均匀的分布。

randrange并且choice都调用相同的方法(_randbelow引擎盖下)。

在Python 3中,choice可以;在Python 2中,它仅接近统一分布,但不能保证均匀分布。由于这是加密货币,因此我依靠栅栏的“绝大部分”,并希望获得这一保证。


1

针对当前主题构建了自己的CLI答案(完整的源代码位于以下URL):

http://0netenv.blogspot.com/2016/08/password-generator-with-argparse.html

使用argparse编写了密码生成器。希望这对某人有帮助(构建密码生成器或使用argparse)!

无论哪种方式,构建都很有趣!

$ ./pwgen.py -h
usage: pwgen.py [-h] [-c COUNT] [-a] [-l] [-n] [-s] [-u] [-p]

 Create a random password
 Special characters, numbers, UPPERCASE -"Oscar",
 and lowercase -"lima" to avoid confusion.
 Default options (no arguments): -c 16 -a
                Enjoy! --0NetEnv@gmail.com

optional arguments:
  -h, --help            show this help message and exit
  -c COUNT, --count COUNT
                        password length
  -a, --all             same as -l -n -s -u
  -l, --lower           include lowercase characters
  -n, --number          include 0-9
  -s, --special         include special characters
  -u, --upper           include uppercase characters
  -p, --license         print license and exit

这是代码:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

license = """
#  pwgen -- the pseudo-random password generator 
#
#  This software is distributed under the MIT license.
#    
#  The MIT License (MIT)
#
#  Copyright (c) 2016 0NetEnv 0netenv@gmail.com
#  Permission is hereby granted, free of charge, to any 
#  person obtaining a copy of this software and associated 
#  documentation files (the "Software"), to deal in the 
#  Software without restriction, including without 
#  limitation the rights to use, copy, modify, merge, 
#  publish, distribute, sublicense, and/or sell copies 
#  of the Software, and to permit persons to whom the 
#  Software is furnished to do so, subject to the following 
#  conditions:
#
#  The above copyright notice and this permission notice 
#  shall be included in all copies or substantial portions 
#  of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 
#  ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
#  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
#  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
#  SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
#  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
#  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 
#  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
#  DEALINGS IN THE SOFTWARE.
#  
#  NOTE:
#  This software was tested on Slackware 14.2, Raspbian, & 
#  Mac OS X 10.11
#
"""

import string
import random
import sys
# first time using argparse library
import argparse
# wanted to change the formatting of the help menu a little bit, so used RawTextHelpFormatter directly
from argparse import RawTextHelpFormatter

typo = ''
c = 16
counter = 0
line = '-' * 40

# CREATE FUNCTION for PWGEN
def pwgen(z, t):
    # EMPTY SET OF CHARACTERS
    charsset = ''
    # UPPERCASE -"O"
    U = 'ABCDEFGHIJKLMNPQRSTUVWXYZ'
    # lowercase -"l"
    L = 'abcdefghijkmnopqrstuvwxyz'
    N = '0123456789'
    S = '!@#$%^&*?<>'

    # make sure we're using an integer, not a char/string
    z = int(z)
    for type in t:
        if 'u' in t:
            charsset = charsset + U
        if 'l' in t:
            charsset = charsset + L
        if 'n' in t:
            charsset = charsset + N
        if 's' in t:
            charsset = charsset + S
        if 'a' == t:
            charsset = charsset + U + L + N + S

    return ''.join(random.choice(charsset) for _ in range(0, int(z)))

# GET ARGUMENTS using ARGPARSE
parser = argparse.ArgumentParser(description='\n Create a random password\n\
 Special characters, numbers, UPPERCASE -"Oscar",\n\
 and lowercase -"lima" to avoid confusion.\n\
 Default options (no arguments): -c 16 -a\n\
 \t\tEnjoy! --0NetEnv@gmail.com', formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("-c", "--count", dest="count", action="store", help="password length")
parser.add_argument("-a", "--all", help="same as -l -n -s -u", action="store_true")
parser.add_argument("-l", "--lower", help="include lowercase characters", action="store_true")
parser.add_argument("-n", "--number", help="include 0-9", action="store_true")
parser.add_argument("-s", "--special", help="include special characters", action="store_true")
parser.add_argument("-u", "--upper", help="include uppercase characters", action="store_true")
parser.add_argument("-p", "--license", help="print license and exit", action="store_true")

# COLLECT ARGPARSE RESULTS
results = args = parser.parse_args()

# CHECK RESULTS
# Check that a length was given.
# If not, gripe and exit.
if args.count == '0':
    print ("Input error:\nCannot create a zero length password.\nExiting")
    exit (0)
# check character results and add to counter if 
# selection is made.
if args.lower:
    typo = typo + 'l'
    counter = counter + 1
    #print "lower"
if args.number:
    typo = typo + 'n'
    counter = counter + 1
    #print "number"
if args.special:
    typo = typo + 's'
    counter = counter + 1
    #print "special"
if args.upper:
    typo = typo + 'u'
    counter = counter + 1
    #print "upper"
if args.all:
    typo = 'a'
    counter = counter + 1
    #print "all"
if args.license:
    print (license)
    exit (1)

# CHECK COUNTER
# Check our counter and see if we used any command line 
# options. We don't want to error out.
# try it gracefully. If no arguments are given, 
# use defaults and tell the user.
# args.count comes from argparse and by default requires
# an input to '-c'. We want to get around that for the 
# sake of convenience.
# Without further adieu, here's our if statement:
if args.count:
    if counter == 0:
        typo = 'a'
        print ("defaulting to '--all'")
    print (line)
    print (pwgen(results.count,typo))
else:
    if counter == 0:
        typo = 'a'
        print ("defaulting to '--count 16 --all'")
    print (line)
    print (pwgen(c,typo))
print (line)
#print typo


1

我喜欢语言学,在我的方法中,我通过交替辅音和元音来创建具有高熵水平的令人难忘的伪单词。

  • 不易受到字典攻击
  • 引人注目,因此难忘的好机会
  • 短密码强度高
  • 可选参数,用于添加随机数字以实现兼容性(记忆力较差,但符合使用旧密码安全性思想构建的应用,例如,需要输入数字)

Python代码:

import random
import string


def make_pseudo_word(syllables=5, add_number=False):
    """Create decent memorable passwords.

    Alternate random consonants & vowels
    """
    rnd = random.SystemRandom()
    s = string.ascii_lowercase
    vowels = 'aeiou'
    consonants = ''.join([x for x in s if x not in vowels])
    pwd = ''.join([rnd.choice(consonants) + rnd.choice(vowels)
               for x in range(syllables)]).title()
    if add_number:
        pwd += str(rnd.choice(range(10)))
    return pwd


>>> make_pseudo_word(syllables=5)
'Bidedatuci'
>>> make_pseudo_word(syllables=5)
'Fobumehura'
>>> make_pseudo_word(syllables=5)
'Seganiwasi'
>>> make_pseudo_word(syllables=4)
'Dokibiqa'
>>> make_pseudo_word(syllables=4)
'Lapoxuho'
>>> make_pseudo_word(syllables=4)
'Qodepira'
>>> make_pseudo_word(syllables=3)
'Minavo'
>>> make_pseudo_word(syllables=3)
'Fiqone'
>>> make_pseudo_word(syllables=3)
'Wiwohi'

缺点:

  • 适用于讲拉丁语和德语的人以及熟悉英语的人
  • 一个人应该使用与应用程序用户或焦点小组有关的语言的元音和辅音

0

这是另一种实现(python 2;需要一些小的重写才能使其在3中工作),它比OJW的快得多,尽管注释/含义相反,但它似乎在每个单词的字典中循环。在我的计算机上使用80,000 IOP SSD的OJW脚本的时间:

real    0m3.264s
user    0m1.768s
sys     0m1.444s

以下脚本将整个字典加载到列表中,然后使用OJW的正则表达式进行过滤,根据索引值的随机选择来选择单词。

这还会生成10个密码短语集,允许传递命令行参数来调整单词数,并添加数字和符号填充(也可以调整长度)。

此脚本的采样时间:

real    0m0.289s
user    0m0.176s
sys     0m0.108s

用法:xkcdpass-mod.py 2 4(例如,这些是默认值)。

它在输出中打印空格以便于阅读,尽管我几乎从未遇到过允许使用它们的在线服务,因此我将忽略它们。可以肯定地用argparse或getopt清除它,并允许开关是否包含空格,包括/不包括符号,大写字母等,以及一些其他的重构,但是我还没有做到这一点。因此,事不宜迟:

#!/usr/bin/env python
#Copyright AMH, 2013; dedicated to public domain.
import os, re, sys, random
from sys import argv

def getargs():
    if len(argv) == 3:
        numwords = argv[1]
        numpads = argv[2]
        return(numwords, numpads)
    elif len(argv) == 2:
        numwords = argv[1]
        numpads = 4
        return (numwords, numpads)
    else:
        numwords = 2
        numpads = 4
        return (numwords, numpads)

def dicopen(dictionary="/usr/share/dict/american-english"):
    f = open(dictionary, "r")
    dic = f.readlines()
    return dic

def genPassword(numwords, numpads):
    r = random.SystemRandom()
    pads = '0123456789!@#$%^&*()'
    padding = []
    words = dicopen()
    wordlist = []
    for i in range (0,int(numpads)):
        padding.append(pads[r.randint(0,len(pads)-1)])
    #initialize counter for only adding filtered words to passphrase
    j = 0
    while (j < int(numwords)):
        inclusion_criteria = re.compile('^[a-z]{5,10}$')
        #Select a random number, then pull the word at that index value, rather than looping through the dictionary for each word
        current_word = words[r.randint(0,len(words)-1)].strip()
        #Only append matching words
        if inclusion_criteria.match(current_word):
            wordlist.append(current_word)
            j += 1
        else:
        #Ignore non-matching words
            pass
    return(" ".join(wordlist)+' '+''.join(padding))

if(__name__ == "__main__"):
    for i in range (1,11):
       print "item "+str(i)+"\n"+genPassword(getargs()[0], getargs()[1])

样本输出:

[✗]─[user@machine]─[~/bin]
└──╼ xkcdpass-mod.py
item 1
digress basketball )%^)
item 2
graves giant &118
item 3
impelled maniacs ^@%1

并准备完整的“正确的马电池钉”(CHBS),无需填充:

┌─[user@machine]─[~/bin]
└──╼ xkcdpass-mod.py 4 0
item 1
superseded warred nighthawk rotary 
item 2
idealize chirruping gabbing vegan 
item 3
wriggling contestant hiccoughs instanced 

根据https://www.grc.com/haystack.htm,出于所有实际目的,假设每秒100万亿次猜测(即100 TH / s),较短的版本大约需要50-60百万个世纪才能破解。完整的CHBS = 1.24万亿亿个世纪; 此外,还有15.51万亿亿亿个世纪的填充。

即使征服了整个比特币采矿网络(在撰写本文时,速度约为2500 TH / s),短版本也可能需要花费250-3亿年的时间才能破解,这对于大多数用途而言可能是足够安全的。


您对“ 50-60百万个世纪来破解”密码短语“ graves Giant&118”(显然是)的估计是一个巨大的错误。您必须假设(就像Randall一样),攻击者正在以与您相同的方式生成猜测-通过从字典中选择随机单词。Randall甚至可以为4个字计算出44位的熵。同样,花费额外的3秒钟来运行这样的程序几乎没有关系。
nealmcb 2014年

而且您的键盘很难记住但很容易破解,因为每个字符只有20种可能(小于小写字母!),因此每个熵只有约4位。给定您的破解速度假设,破解带有Randall的具有44位熵的4字密码将花费不到一秒钟的时间,并且像您的2字示例加4个随机填充符一样破解一个密码要容易得多(仅22 + 4 * 4 = 38位的熵)。
nealmcb 2014年


0

有点偏离主题,但是我也使用TKinter做到了这一点。希望它可以帮助:

import os, random, string
from tkinter import *

def createPwd():
    try:
        length = int(e1.get())
    except ValueError:
        return
    chars = string.ascii_letters + string.digits + '!@#$%^&*()?\/'
    random.seed = (os.urandom(1024))
    e2.config(state=NORMAL)
    e2.delete(0,'end')
    e2.insert(0,''.join(random.choice(chars) for i in range(length)))
    e2.config(state="readonly")

mainWindow = Tk()
mainWindow.title('Password generator')

mainWindow.resizable(0,0)

f0 = Frame(mainWindow)

f0.pack(side=TOP,pady=5,padx=5,fill=X,expand=1)

Label(f0,text="Length: ",anchor=E).grid(row=0,column=0,sticky=E)

e1 = Entry(f0)
e1.insert(0,'12')
e1.grid(row=0,column=1)

btn = Button(f0,text="Generate")
btn['command'] = lambda: createPwd()
btn.grid(row=0,column=2,rowspan=1,padx=10,ipadx=10)

Label(f0,text="Generated password: ",anchor=E).grid(row=1,column=0,sticky=E)
e2 = Entry(f0)
e2.grid(row=1,column=1)

createPwd()

#starting main window
mainWindow.mainloop()

0

这是一个简单的小型程序,面向无法为自己的公共帐户提供安全密码的人。

只需在命令控制台上运行该程序,然后传入一堆您似乎熟悉的字母,它就会根据您插入的内容生成一系列符号。

当然,该程序不支持多序列生成。

您可以从我的github pull下载代码:https : //github.com/abdechahidely/python_password_generator

from string import ascii_lowercase, ascii_uppercase, digits, punctuation
from random import randint, choice, shuffle
from math   import ceil
from re     import finditer

lower_cases  = ascii_lowercase
upper_cases  = ascii_uppercase
lower_upper  = dict(zip(lower_cases, upper_cases))
upper_lower  = dict(zip(upper_cases, lower_cases))
punctuations = '#$%&@!?.'
space        = ' '

class PunctOrDigit():

    def __init__(self, number_of_punctuations, number_of_digits):
        self.puncts = number_of_punctuations
        self.digits = number_of_digits
        self.dupl_puncts = self.puncts
        self.dupl_digits = self.digits

    def PorD(self):
        symbol_type = choice('pd')
        if symbol_type == 'p':
            if self.puncts == 0:
                return 'd'
            else:
                self.puncts -= 1
                return symbol_type
        if symbol_type == 'd':
            if self.digits == 0:
                return 'p'
            else:
                self.digits -= 1
                return symbol_type

    def reset(self):
        self.puncts = self.dupl_puncts
        self.digits = self.dupl_digits

def is_empty(text):
    for symbol in text:
        if symbol != space:
            return False
    return True

def contain_unauthorized_symbols(text):
    for symbol in text:
        if symbol in punctuation or symbol in digits:
            return True
    return False

def user_input():
    user_input = input('-- Sentence to transform: ')
    while is_empty(user_input) or len(user_input) < 8 or contain_unauthorized_symbols(user_input):
        user_input = input('-- Sentence to transform: ')
    return user_input

def number_of_punctuations(text):
    return ceil(len(text) / 2) - 3

def number_of_digits(text):
    return ceil(len(text) / 2) - 2

def total_symbols(text):
    return (number_of_digits(text) + number_of_punctuations(text), 
            number_of_punctuations(text),
            number_of_digits(text))

def positions_to_change(text):
    pos_objct = PunctOrDigit(number_of_punctuations(text), number_of_digits(text))
    positions = {}
    while len(positions) < total_symbols(text)[0]:
        i = randint(0,len(text)-1)
        while i in positions:
            i = randint(0,len(text)-1)
        positions[i] = pos_objct.PorD()
    pos_objct.reset()
    return positions

def random_switch(letter):
    if letter in lower_cases:
        switch_or_pass = choice('sp')
        if switch_or_pass == 's': return lower_upper[letter]
        else:                     return letter
    if letter in upper_cases:
        switch_or_pass = choice('sp')
        if switch_or_pass == 's': return upper_lower[letter]
        else:                     return letter

def repeated(text):
    reps = {}
    for letter in set(list(text)):
        indexs = [w.start() for w in finditer(letter, text)]
        if letter != ' ':
            if len(indexs) != 1:
                reps[letter] = indexs
    return reps

def not_repeated(text):
    reps = {}
    for letter in set(list(text)):
        indexs = [w.start() for w in finditer(letter, text)]
        if letter != ' ':
            if len(indexs) == 1:
                reps[letter] = indexs
    return reps

def generator(text, positions_to_change):
    rep     = repeated(text)
    not_rep = not_repeated(text)
    text    = list(text)

    for x in text:
        x_pos = text.index(x)
        if x not in positions_to_change:
            text[x_pos] = random_switch(x)

    for x in rep:
        for pos in rep[x]:
            if pos in positions_to_change:
                if positions_to_change[pos] == 'p':
                    shuffle(list(punctuations))
                    text[pos] = choice(punctuations)
                if positions_to_change[pos] == 'd':
                    shuffle(list(digits))
                    text[pos] = choice(digits)
    for x in not_rep:
        for pos in not_rep[x]:
            if pos in positions_to_change:
                if positions_to_change[pos] == 'p':
                    shuffle(list(punctuations))
                    text[pos] = choice(punctuations)
                if positions_to_change[pos] == 'd':
                    shuffle(list(digits))
                    text[pos] = choice(digits)

    text = ''.join(text)
    return text

if __name__ == '__main__':
    x = user_input()
    print(generator(x, positions_to_change(x)))

0

这是研究此主题后的随机密码生成器:

`import os, random, string
   #Generate Random Password
   UPP = random.SystemRandom().choice(string.ascii_uppercase)
   LOW1 = random.SystemRandom().choice(string.ascii_lowercase)
   LOW2 = random.SystemRandom().choice(string.ascii_lowercase)
   LOW3 = random.SystemRandom().choice(string.ascii_lowercase)
   DIG1 = random.SystemRandom().choice(string.digits)
   DIG2 = random.SystemRandom().choice(string.digits)
   DIG3 = random.SystemRandom().choice(string.digits)
   SPEC = random.SystemRandom().choice('!@#$%^&*()')
   PWD = None
   PWD = UPP + LOW1 + LOW2 + LOW3 + DIG1 + DIG2 + DIG3 + SPEC
   PWD = ''.join(random.sample(PWD,len(PWD)))
   print(PWD)`

这将生成一个随机密码,其中包含1个随机大写字母,3个随机小写字母,3个随机数字和1个随机特殊字符-可以根据需要进行调整。然后,它组合每个随机字符并创建一个随机顺序。我不知道这是否被认为是“高质量”,但是可以完成工作。


0

我的解决方案基于@Thomas Pornin的答案(已更新)

import os, string

def get_pass(password_len=12):
  new_password=None
  symbols='+!'
  chars=string.ascii_lowercase+\
        string.ascii_uppercase+\
        string.digits+\
        symbols

  while new_password is None or \
        new_password[0] in string.digits or \
        new_password[0] in symbols:
     new_password=''.join([chars[ord(os.urandom(1)) % len(chars)] \
                             for i in range(password_len)])
  return new_password

print(get_pass())

此函数返回一个随机密码(密码开头没有数字或符号)。


此代码存在严重缺陷,遭受Pornin谈到的相同偏见问题,因为chars列表长度为69个字符,而不是64个字符。前缀“ A”的字符显示的频率仅为其余字符的75%。请参阅@foudfou的答案。此外,它一点也不起作用:最后一行应该是print(get_pass())
nealmcb

0

我最近才开始学习python,这是我今天写的。希望这可以帮助。

import random

characters = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^()}{/<>'
print('Password Length: ')
passwordLength = int(input())
password = ''

for i in range(passwordLength):
    password += random.choice(characters)
print(password)

0
"""
This code below in any shape or form is owned by A.S Gallery 

This code is the asnwer for Password Generator quiz - CodeHs 
This code works 100%
Have fun exploring !!!

"""

# Imports 
import random
import time
print "Hi !!!"
password_output = "this is your new password : "
ask_name = input("Enter your Name : ")
greetings_name = "Hi "+ str(ask_name) + "!!! "
print greetings_name
print "Now we will make your new password using the ULTIMATE password generator !!!"
time.sleep(8)
print "Our password generator will give you multiple choices, you can choose any password generator you want !!! "
time.sleep(8)
print "You can choose if you want a strong password or a weak password !! (strong recommended) "
time.sleep(8)
print "You can also make your own password, to make your own password type own !!! "
time.sleep(8)
print "If you want to choose strong type strong, if weak then type weak !!! "
time.sleep(8)




# Example:
# Returns random number within and including 0 and 10.
def strong_password():
    user_input = int(input("Enter a number : "))
    print type(user_input)
    time.sleep(3)
    # calculate_input = user_input * user_input
    calculate_types = input("Do you want to add, multiply or mod the numbers : ")
    time.sleep(3)
    if calculate_types == "add":
        calculate_input = user_input + user_input
    elif calculate_types == "multiply" :
        calculate_input = user_input * user_input
    elif calculate_types == "mod":
        calculate_input = user_input & user_input 
    else:
        print "Check your spelling and try again :( "

    # Random element in a string
    time.sleep(4)
    want_symbols = input("Do you want symbols ?(Y/N) : ")
    time.sleep(4)
    random_element = random.choice('abcdefg345')
    if want_symbols == "Y":
        random_element2 = random.choice('@#()@*($*(@)(*^()*()(#$)*@#)*((@*()@*#)(*)@*($*(%#*)#(*@@_!_()(')
    elif want_symbols == "N":
        random_element2 = random.choice('29371294203712492703740182903820498201381204AKSJFKSHEHJKFJAODL')
    random_element3 = random.choice('abcdefghiiasudasdjsiasdhwudagsjdgaskdjsafgjasj')
    random_element4 = random.choice('abcdefghijsdhjaskdhkasjdhakdjhaskdasjdhakjsd')
    random_element5 = random.choice('abcdefghijsdhsakjdhsajdldasjdasdjasldas')
    random_elements6 = random.choice('129389230928308290382109830293943827492347')
    random_elements7 = random.choice('2473285473q9mdnuwyr8KSDJKDSJKL932uc3487389473289479h3289wjdi94802w')
    random_elements8 = random.choice('AKDJKAJDKJIKJDUIFHSJHUFRUDIJFDKLDJKDJLJFKLJKLDJLDJKLDJLDJLSKJDKLJDLJDKSLJD')


    time.sleep(8)

    print str(ask_name) + " " + str(password_output) + str(calculate_input) + str(random_element) + str(random_element2) + str(random_element3) + str(random_element4) + str(random_element5) + str(random_elements6) + str(random_elements7) + str(random_elements8)

def weak_password():
    user_input = int(input("Enter a number : "))
    print type(user_input)
    time.sleep(3)
    # calculate_input = user_input * user_input
    calculate_types = input("Do you want to add, multiply or mod the numbers : ")
    time.sleep(3)
    if calculate_types == "add":
        calculate_input = user_input + user_input
    elif calculate_types == "multiply" :
        calculate_input = user_input * user_input
    elif calculate_types == "mod":
        calculate_input = user_input & user_input 
    else:
        time.sleep(3)
        print "Check your spelling and try again :( "

    # Random element in a string


    random_ness = random.choice("ABCDEFGHI*(#*#$()#*$)(E)(UWIJEDSH(*#U$()UDSLKH)UW*)$(*&#*(YE(*DY#*YUHSLDF:LKDDSDK")
    my_tuple = (calculate_input, random_ness, user_input, ask_name)
    new_tuple = my_tuple[1] 
    new_tuple1 = my_tuple[2]
    new_tuple2 = my_tuple[3]
    time.sleep(7)
    print str(ask_name) + str(password_output) + str(new_tuple) + str(new_tuple1) + str(new_tuple2) 

def own_password():
    my_list = []
    ask_times = int(input("How many characters do you want ? (between 1 - 8) : "))
    time.sleep(10)
    if ask_times > 8:
        print "Invalid Request"
    elif ask_times < 1:
        print "Invalid Request"
    else:
        time.sleep(2)
        print "You CANNOT include symbols or numbers in this option !!! "
        for i in range(ask_times):
            user_ask = input("Enter the character: ")
            time.sleep(0.6)
            my_list.append(user_ask)
        own_password =  "".join(map(str,my_list))
        time.sleep(4)
        print "Your own password is : " + own_password




strong_pass = input("Do you want a strong password or a weak one or make your own password !! ? : ")
if strong_pass == "strong":
    strong_password()
elif strong_pass == "weak":
    weak_password()
elif strong_pass == "own":
    own_password()
else :
    print "Invalid Request"

time.sleep(3)
print "Congrats, on creating your best password !!! I belived you used strong password generator because its the BEST !!"
time.sleep(7)
print "If not, no problem just restart the program and type strong when prompted !!! "
time.sleep(6)
print "Have a nice day !"

这段代码也是CodeH测验的答案(如果有的话)!


-1

是的,没有哪个黑客会破解该密码。现在,在此之后,我建议继续您的随机密码生成器项目,并使用Tkinter或Flask创建UI或GUI界面,以便其他人可以使用它。例如,我只是通过搜索“密码生成器python UI”找到了这个不错的小项目。https://passwordgenerator.pythonanywhere.com/

也许您想做类似上面的事情?了解如何将python实施到Web开发中是一项很好的技能。

祝好运。

和平了


-3

这比什么都有趣。在passwordmeter.com上得分很高,但无法记住。

#!/usr/bin/ruby

puts (33..126).map{|x| ('a'..'z').include?(x.chr.downcase) ?
                       (0..9).to_a.shuffle[0].to_s + x.chr :
                       x.chr}.uniq.shuffle[0..41].join[0..41]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.