Высококачественный, простой генератор случайных паролей
Я заинтересован в создании очень простого, высокого (криптографического) генератора случайных паролей качества. Есть ли лучший способ сделать это?
import os, random, string
length = 13
chars = string.ascii_letters + string.digits + '[email protected]#$%^&*()'
random.seed = (os.urandom(1024))
print ''.join(random.choice(chars) for i in range(length))
Ответы
Ответ 1
Трудная вещь с паролями заключается в том, чтобы сделать их достаточно сильными и все еще помнить о них. Если пароль не предназначен для запоминания человеком, то это не пароль.
Вы используете Python os.urandom()
: это хорошо. Для любой практической цели (даже криптографии) вывод os.urandom()
неотличим от истинного alea. Затем вы используете его как семя в random
, что менее полезно: это некриптографический PRNG, и его вывод может содержать некоторую структуру, которая не будет регистрироваться в статистическом инструменте измерения, но может быть использована интеллектуальным злоумышленником, Вы должны работать с os.urandom()
. Чтобы сделать вещи простыми: выберите алфавит длиной 64, например. буквы (в верхнем и нижнем регистре), цифры и два дополнительных символа пунктуации (например, "+" и "/" ). Затем для каждого символа пароля получите один байт из os.urandom()
, уменьшите значение по модулю 64 (это непредвзято, потому что 64 делит 256) и использовать результат как индекс в массиве chars
.
С алфавитом длиной 64, вы получите 6 бит энтропии на символ (потому что 2 6= 64). Таким образом, с 13 символами вы получаете 78 бит энтропии. Во всех случаях это не так сильно, но уже очень сильное (его можно победить с бюджетом, который будет рассчитываться месяцами и миллиардами долларов, а не миллионами).
Ответ 2
XKCD имеет отличное объяснение того, почему то, что вы думаете являются сильными паролями , не являются.
![http://xkcd.com/936/]()
Тем, кто понимает теорию и безопасность информации и находится в беспричинный аргумент с кем-то, кто этого не делает (возможно, смешанный случай), я искренне извиняюсь. - Рэндалл Мунро
И если вы не понимаете математику за тем, что объясняет эта иллюстрация, не пытайтесь писать что-либо, что должно быть криптографически безопасным, потому что этого не будет. Просто поместите мышь и откройте клавиатуру.
Ответ 3
Всего два дня назад, 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 inensorable: ~/devel/inexorable-misc $./mkpass.py 5 12 Ваш пароль "узнал поврежденные сохраненные жилые этапы". Это эквивалентно 60-битовому ключу.
Этот пароль потребует 2.5e + 03 CPU-лет, чтобы взломать мой недорогой Celeron E1200 с 2008 года, предполагая атаку в автономном режиме на хэш-код MS-Cache, который является самым худшим алгоритмом хэширования паролей в общем использовании, немного хуже, чем даже простой MD5.
Самым распространенным алгоритмом хеширования паролей в эти дни является итерация MD5 FreeBSDs; взломать такой хэш займет 5.2e + 06 CPU-years.
Но современный GPU может взломать примерно в 250 раз быстрее, так что тот же самый итерационный MD5 упадет в 2e + 04 GPU-years.
Этот графический процессор стоит около 1,45 доллара США в день для запуска в 2011 году, поэтому взломать пароль будет стоить около 3 долларов США + 09.
Я начал использовать пароль, сгенерированный таким образом вместо 9-печатного ASCII-символьного случайного пароля, который одинаково силен. Утверждение Манро о том, что эти пароли намного легче запомнить, является правильным. Тем не менее, все еще существует проблема: потому что количество символов в энтропии на один символ меньше (около 1.7 вместо 6.6), в пароле много избыточности, и поэтому атаки, такие как атака с временным каналом ssh (Song, Wagner и Tian Herbivore, о которых я узнал из Брэма Коэна в кафе Bagdad в ранние часы утром, много лет назад), и атаки с записью звука на клавиатуре имеют гораздо больше шансов собрать достаточную информацию, чтобы сделать пароль доступным.
Моя контрмера для атаки Herbivore, которая хорошо работает с 9-символьным паролем, но крайне раздражает мой новый пароль, заключается в том, чтобы ввести пароль с задержкой в полсекунды между символами, так что канал синхронизации не слишком много информация о фактических используемых символах. Кроме того, более низкая длина 9-символьного пароля по своей сути дает подход Herbivore гораздо меньше информации, чтобы пережевывать.
Другие возможные контрмеры включают использование режима оболочки Emacs, который запрашивает вас локально для пароля, когда он распознает приглашение пароля, а затем сразу же отправляет весь пароль и копирует и вставляет пароль из другого места.
Как и следовало ожидать, этот пароль также занимает немного больше времени: примерно 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 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 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))
Ответ 4
реализация решения @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
Ответ 5
Другая реализация метода 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
Ответ 6
Я знаю, что этот вопрос был опубликован еще в 2011 году, но для тех, кто придет к нему сейчас, в 2014 году и далее, я хочу сказать одно: ПРОТИВОСТОЯТЬ НАСТОЯЩЕМУ, ЧТОБЫ ПОВЫСИТЬ РЕМОНТ КОЛЕСА.
В этих ситуациях вам лучше всего искать программное обеспечение с открытым исходным кодом, например, ограничить свой поиск результатами github. Безусловно лучшее, что я нашел:
https://github.com/redacted/XKCD-password-generator
Ответ 7
Вы не можете доверять генератору псевдослучайных чисел python при генерации пароля. Это не обязательно криптографически случайное. Вы высеваете генератор псевдослучайных чисел из os.urandom
, который является хорошим началом. Но после этого вы зависите от генератора питона.
Лучшим выбором будет random.SystemRandom()
класс, который принимает случайные числа из того же источника, что и urandom
. Согласно документации на python, которая должна быть достаточно хороша для использования в криптографии. Класс SystemRandom
дает вам все, что делает основной случайный класс, но вам не нужно беспокоиться о псевдослучайности.
Пример кода с использованием random.SystemRandom(для Python 2.6):
import random, string
length = 13
chars = string.ascii_letters + string.digits + '[email protected]#$%^&*()'
rnd = random.SystemRandom()
print ''.join(rnd.choice(chars) for i in range(length))
Примечание. Ваш пробег может отличаться. В документации на Python указано, что random.SystemRandom зависит от операционной системы.
Ответ 8
Учитывая ваш комментарий,
Мне просто нужно создать пароли, которые более безопасны, чем те, которые я придумал в своей голове.
Кажется, вы хотите использовать свою программу для генерации паролей, а не просто писать ее как упражнение. Предпочтительно использовать существующую реализацию, потому что, если вы допустили ошибку, выход может быть скомпрометирован. Читайте о атаках генератора случайных чисел; в частности, известная ошибка RNG в Debian подвергла действию закрытые ключи SSL.
Поэтому вместо этого рассмотрите возможность использования pwgen
. Он предоставляет несколько опций, которые вы должны выбрать в зависимости от того, на что вы собираетесь использовать пароли.
Ответ 9
Implenting @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))
Ответ 10
Это легко:)
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/
Ответ 11
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='[email protected]#$%'))
- Создает пароли, которые вы можете запомнить
- Использует
os.urandom()
- Управляет правилами реального мира, такими как добавление чисел, прописных букв и символов.
Конечно, его можно улучшить, но это то, что я использую.
Ответ 12
Таким образом работает. Это прекрасно. Если у вас есть дополнительные правила, такие как исключение слов словаря, то вы можете также включить эти фильтры, но вероятность случайного создания словарного слова с этой установкой крайне мала.
Ответ 13
В вашей реализации есть некоторые проблемы:
random.seed = (os.urandom(1024))
Это не засевает генератор случайных чисел; он заменяет функцию seed
байтом. Вам нужно позвонить seed
, например, random.seed(…)
.
print ''.join(random.choice(chars) for i in range(length))
PRNG по умолчанию Python - это 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 и делает гораздо лучшую работу по обеспечению единообразия. Документы Python 3 для randrange
отмечают,
Изменено в версии 3.2: randrange()
более сложна в создании одинаково распределенных значений. Раньше он использовал стиль типа int(random()*n)
, который мог создавать слегка неравномерные распределения.
randrange
и choice
оба вызова того же метода (_randbelow
) под капотом.
В Python 3, choice
отлично; в Python 2 он приближается только к равномерному распределению, но не гарантирует его. Поскольку это криптография, я опираюсь на сторону "не рискую" забор и хотел бы получить эту гарантию.
Ответ 14
Построил мой собственный ответ 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 = '[email protected]#$%^&*?<>'
# 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 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
Ответ 15
Я люблю лингвистику, в моем подходе я создаю запоминающиеся псевдо-слова с высоким уровнем энтропии чередующимися согласными и гласными.
- Не подвержен атакам по словарю
- Произносимый и, следовательно, хороший шанс быть запоминающимся
- Короткие пароли с приличной силой
- Необязательный параметр для добавления случайной цифры для совместимости (менее запоминающийся, но соответствует приложениям, созданным с учетом концепции безопасности старого пароля, например, требующей цифры)
Код 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'
Минусы:
- для носителей латинского и германского языков и тех, кто знаком с английским
- нужно использовать гласные и согласные языка, преобладающего с пользователями приложения или фокус-группой, и настраивать
Ответ 16
Вот еще одна реализация (python 2; для ее работы в 3) потребуется немного мелких перезаписей, которая намного быстрее, чем OJW, которая, кажется, перебирает словарь для каждого слова, несмотря на комментарий/импликацию об обратном. Сроки OJW script на моей машине с 80 000 IOP SSD:
real 0m3.264s
user 0m1.768s
sys 0m1.444s
Следующий script загружает весь словарь в список, затем выбирает слова на основе случайного выбора значения индекса, используя регулярное выражение OJW для фильтрации.
Это также генерирует 10 наборов парольной фразы, позволяет передавать параметры командной строки для корректировки количества слов и добавляет количество и заполнение символов (также регулируемая длина).
Пример времени для этого script:
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 = '[email protected]#$%^&*()'
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])
Пример вывода:
[✗]─[[email protected]]─[~/bin]
└──╼ xkcdpass-mod.py
item 1
digress basketball )%^)
item 2
graves giant &118
item 3
impelled maniacs ^@%1
И для полного "правильного штатива батареи лошади" (CHBS), нет прокладки:
┌─[[email protected]]─[~/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 трлн триллион триллионов столетий.
Даже привлекая всю интеллектуальную сеть Bitcoin (~ 2500 TH/s на момент написания этой статьи), короткая версия, вероятно, займет 250-300 миллионов лет, что, вероятно, достаточно безопасно для большинства целей.
Ответ 17
import uuid
print('Your new password is: {0}').format(uuid.uuid4())
Ответ 18
Немного от темы, но я сделал это, используя также TKinter. Надеюсь, это поможет:
import os, random, string
from tkinter import *
def createPwd():
try:
length = int(e1.get())
except ValueError:
return
chars = string.ascii_letters + string.digits + '[email protected]#$%^&*()?\/'
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()
Ответ 19
Base64 позволяет кодировать двоичные данные в режиме чтения/записи на человека без потери данных.
import os
random_bytes=os.urandom(12)
secret=random_bytes.encode("base64")
Ответ 20
Это простая небольшая программа, адресованная людям, которые не могут определить безопасные пароли для собственных публичных учетных записей.
Просто запустите программу на командной консоли и передайте кучу писем, которые вам знакомы, и она будет генерировать последовательность символов на основе того, что вы вставили.
конечно, программа не поддерживает генерации нескольких последовательностей.
Вы можете загрузить код из моего 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)))
Ответ 21
Вот мой генератор случайных паролей после исследования этой темы:
`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('[email protected]#$%^&*()')
PWD = None
PWD = UPP + LOW1 + LOW2 + LOW3 + DIG1 + DIG2 + DIG3 + SPEC
PWD = ''.join(random.sample(PWD,len(PWD)))
print(PWD)`
Это приведет к созданию случайного пароля с 1 случайной буквой в верхнем регистре, 3 случайными строчными буквами, 3 случайными цифрами и 1 случайным специальным символом - это можно отрегулировать по мере необходимости. Затем он объединяет каждый случайный символ и создает случайный порядок. Я не знаю, считается ли это "высоким качеством", но он выполняет свою работу.
Ответ 22
Мое решение основано на ответе @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())
Эта функция возвращает случайный пароль (без цифры или символа в начале пароля).
Ответ 23
Я только недавно начал изучать Python, и это то, что я написал сегодня. Надеюсь это поможет.
import random
characters = '[email protected]#$%^()}{/<>'
print('Password Length: ')
passwordLength = int(input())
password = ''
for i in range(passwordLength):
password += random.choice(characters)
print(password)
Ответ 24
Это больше для удовольствия, чем что-либо.
Оценки выгодно в 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]