私は、非常にシンプルで高品質の(暗号化)品質のランダムパスワードジェネレーターを作成することに興味があります。これを行うためのより良い方法はありますか?
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))
パスワードの難しさは、パスワードを十分に強くし、それでも覚えられるようにすることです。パスワードが人間によって記憶されることを意図していない場合、それは実際にはパスワードではありません。
Pythonのos.urandom()
を使用します:それは良いことです。実用的な目的(暗号化を含む)であれば、os.urandom()
の出力は本当の意味と区別がつきません。次に、それをrandom
のシードとして使用します。これはあまり良くありません。1つは非暗号化PRNGであり、その出力は統計測定ツールに登録されないが、インテリジェントな攻撃者。 os.urandom()
をずっと使用してください。物事を簡単にするには:長さ64のアルファベットを選択します。文字(大文字と小文字)、数字、および2つの余分な句読文字(「+」や「/」など)。次に、各パスワード文字に対して、os.urandom()
から1バイトを取得し、64を法とする値を減らし(64は256を除算するため不偏です)、chars
配列のインデックスとして結果を使用します。
長さ64のアルファベットを使用すると、文字ごとに6ビットのエントロピーが得られます(26 = 64)。したがって、13文字では、78ビットのエントロピーが得られます。これはすべての場合に最終的に強力なわけではありませんが、すでに非常に強力です(数百万ドルではなく、数ヶ月と数十億ドルでカウントされる予算で打ち負かされる可能性があります)。
[〜#〜] xkcd [〜#〜] には、なぜあなたが考えるものが強力なパスワードではない。
情報理論とセキュリティを理解し、(おそらく大文字と小文字が混在する可能性のない)誰かと腹立たしい議論をしている人には、心から謝罪します。 -ランドール・マンロー
この図が説明していることの背後にある数学 を理解していない場合、暗号的に安全であるはずの何かを書いてはいけません。マウスを下に置き、キーボードから離れます。
ほんの2日前、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 in inexorable:〜/ devel/inexorable-misc $ ./mkpass.py 5 12あなたのパスワードは「学習ダメージで保存されたレジデンシャルステージ」です。これは60ビットキーに相当します。
一般的な最悪のパスワードハッシュアルゴリズムであるMS-Cacheハッシュに対するオフライン攻撃を想定すると、そのパスワードは2008年から安価なCeleron E1200をクラックするのに2.5e + 03 CPU年かかります。
最近の最も一般的なパスワードハッシュアルゴリズムは、FreeBSDの反復MD5です。このようなハッシュのクラッキングには5.2e + 06 CPU年かかります。
しかし、最新のGPUは約250倍の速度でクラッキングできるため、同じ反復MD5は2e + 04 GPU年になります。
そのGPUは2011年に実行するために1日あたり約1.45ドルかかるため、パスワードを解読するには約3e + 09ドルかかります。
同様に強力な9文字のASCII文字のランダムパスワードの代わりに、この方法で生成されたパスワードを使用し始めました。これらのパスワードは記憶しやすいというマンローの主張は正しいです。ただし、まだ問題があります:文字ごとのエントロピーのビットがはるかに少ないため(6.6ではなく1.7)、パスワードに多くの冗長性があり、sshタイミングチャネル攻撃(歌、ワーグナー、および数年前のある朝の早朝にバグダッドカフェのブラムコーエンから学んだTian Herbivore攻撃とキーボードオーディオ録音攻撃は、パスワードを攻撃可能にするのに十分な情報を取得する可能性がはるかに高いです。
Herbivore攻撃に対する私の対策は、9文字のパスワードでうまく機能しますが、新しいパスワードでは非常に迷惑です。文字間の0.5秒の遅延でパスワードを入力し、タイミングチャネルが実際に使用される文字。さらに、9文字のパスワードの長さが短いため、本質的に草食動物のアプローチでは噛む情報がはるかに少なくなります。
他の考えられる対策には、Emacs Shellモードを使用する方法があります。これは、パスワードプロンプトを認識するとローカルでパスワードを要求し、一度にパスワード全体を送信します。
ご想像のとおり、このパスワードの入力には少し時間がかかります。約3秒ではなく約6秒です。
#!/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 __== '__main__':
sys.exit(main(sys.argv))
@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
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(__== "__main__"):
print genPassword()
サンプル出力:
$ ./randompassword.py
affluent afford scarlets twines
$ ./randompassword.py
speedboat ellipse further staffer
この質問は2011年に投稿されたことは知っていますが、2014年以降にこの質問に来た人たちのために、私は1つ言いたいことがあります。車輪を再生するために急いでください。
これらの状況では、オープンソースソフトウェアを検索することをお勧めします。たとえば、検索結果をgithubの結果に制限します。私が見つけた断然最高のもの:
パスワードを生成するときに、Pythonの擬似乱数ジェネレーターを信頼することはできません。必ずしも暗号的にランダムではありません。 _os.urandom
_から擬似乱数ジェネレーターをシードしているので、これは良いスタートです。しかし、その後はPythonのジェネレーターに依存します。
より良い選択は、urandom
と同じソースから乱数を取得する random.SystemRandom()
クラスです。 pythonドキュメントによると、暗号化の使用に十分なはずです。SystemRandom
クラスは、メインのランダムクラスが行うすべてを提供しますが、擬似乱数。
Random.SystemRandomを使用したコード例(Python 2.6)の場合:
_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の可用性はオペレーティングシステムによって異なると書かれています。
あなたのコメントを考慮して、
頭の中で思いつくものよりも安全なパスワードを生成できればいいだけです。
単に演習として書くのではなく、プログラムを使用してパスワードを生成したいようです。間違いを犯すと出力が損なわれる可能性があるため、既存の実装を使用することをお勧めします。 乱数ジェネレーター攻撃 ;について読む特に、Debianの有名なRNGバグは人々のSSL秘密鍵をさらしました。
その代わりに、 pwgen
の使用を検討してください。パスワードを使用する予定に応じて選択する必要があるオプションがいくつかあります。
簡単です :)
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
そして、do:
print codegenerator()
ありがとう http://xkcd.com/936/
@Thomas Porninソリューションの実装:(@Yossiの不正確な答えはコメントできません)
import string, os
chars = string.letters + string.digits + '+/'
assert 256 % len(chars) == 0 # non-biased later modulo
PWD_LEN = 16
print ''.join(chars[ord(c) % len(chars)] for c in os.urandom(PWD_LEN))
_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 __== '__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()
を使用します確かに改善できますが、これは私が使用するものです。
当面のトピックに対する独自の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! [email protected]
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 [email protected]
# 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! [email protected]', 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
そのように動作します。それはまったく問題ありません。辞書の単語を除外するなどの追加のルールがある場合、それらのフィルターも含めることができますが、その設定で辞書の単語をランダムに生成する可能性は非常に小さいです。
私は言語学が大好きです。私のアプローチでは、子音と母音を交互に繰り返すことで、高レベルのエントロピーを持つ記憶に残る疑似単語を作成します。
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'
短所:
実装にはいくつかの問題があります。
random.seed = (os.urandom(1024))
これは、乱数ジェネレーターをシードしません。 seed
関数をバイト文字列に置き換えます。 random.seed(…)
などの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
…inPython 2。残念ながら、ここのself.random
はC関数なので、これは見づらくなっています。ここでのコードの匂いは、このコードがほぼ確実に均一に選択しないということです。コードはPython 3で完全に変更されており、均一性を確保するためにより良い仕事をしています。 Python randrange
に関する3つのドキュメント注、
バージョン3.2で変更:
randrange()
は、均等に分散された値の生成に関してより洗練されています。以前は、int(random()*n)
のようなスタイルを使用していたため、わずかに不均一な分布が生じる可能性がありました。
randrange
とchoice
は両方とも、内部で同じメソッド(_randbelow
)を呼び出します。
Python 3では、choice
で問題ありません。 Python 2では、均一分布にcloseだけが来ますが、それを保証するものではありません。これは暗号であるため、私はフェンスの「チャンスをとらない」側に頼り、その保証を持ちたいと思っています。
私は最近学習を始めましたpythonそしてこれは私が今日書いたものです。これが役に立てば幸いです。
import random
characters = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^()}{/<>'
print('Password Length: ')
passwordLength = int(input())
password = ''
for i in range(passwordLength):
password += random.choice(characters)
print(password)
import uuid
print('Your new password is: {0}').format(uuid.uuid4())
トピックから少し外れていますが、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()
ここに別の実装があります(Python2。3で動作させるためにいくつかのマイナーリライトが必要です)。これは、反対のコメント/意味にもかかわらず、各Wordの辞書をループするように見える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(__== "__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 によると、すべての実用的な目的で、1秒間に100兆回の推測(つまり、100 TH/s)を想定すると、短いバージョンでは約50-クラックする6000万世紀。完全なCHBS = 1.24百兆兆世紀。それにパディングを加えると、15.51兆兆兆兆世紀になります。
Bitcoinマイニングネットワーク全体(この記事の執筆時点で〜2500 TH/s)に参加したとしても、短いバージョンは壊れるまでに2億5億から3億年かかる可能性があり、おそらくほとんどの目的に対して十分に安全です。
これは、自分のパブリックアカウントの安全なパスワードがわからない人向けのシンプルな小さなプログラムです。
コマンドコンソールでプログラムを実行し、使い慣れた文字の束を渡すだけで、挿入した内容に基づいて一連の記号が生成されます。
もちろん、プログラムは複数のシーケンスの生成をサポートしていません。
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 __== '__main__':
x = user_input()
print(generator(x, positions_to_change(x)))
Base64では、データを失うことなく、人間が読み取り/書き込み可能なモードでバイナリデータをエンコードできます。
import os
random_bytes=os.urandom(12)
secret=random_bytes.encode("base64")
@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())
この関数は、ランダムなパスワードを返します(パスワードの先頭に数字や記号はありません)。
このトピックを調査した後のランダムパスワードジェネレーターを次に示します。
`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つのランダムな特殊文字を含むランダムなパスワードが生成されます。これは必要に応じて調整できます。次に、各ランダムな文字を組み合わせて、ランダムな順序を作成します。これが「高品質」と見なされるかどうかはわかりませんが、仕事は完了です。