Je suis intéressé par la création d'un générateur de mot de passe aléatoire de qualité très simple et de haute qualité (cryptographique). Y a-t-il une meilleure manière de faire cela?
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))
La difficulté avec les mots de passe est de les rendre suffisamment forts et de pouvoir les mémoriser. Si le mot de passe n'est pas destiné à être mémorisé par un être humain, il ne s'agit pas vraiment d'un mot de passe.
Vous utilisez os.urandom()
: c'est bien. À toutes fins pratiques (même la cryptographie), la sortie de os.urandom()
est indiscernable de la réalité. Ensuite, vous l’utilisez en tant que graine dans random
, ce qui est moins bon: il s’agit d’un PRNG non cryptographique et sa sortie peut présenter une structure qui ne sera pas enregistrée dans un outil de mesure statistique, mais qui pourrait être exploitée par un attaquant intelligent. Vous devriez travailler avec os.urandom()
tout au long. Pour simplifier les choses: choisissez un alphabet de longueur 64, par exemple. lettres (majuscules et minuscules), chiffres et deux caractères de ponctuation supplémentaires (tels que '+' et '/'). Ensuite, pour chaque caractère de mot de passe, obtenez un octet à partir de os.urandom()
, réduisez la valeur modulo 64 (ceci est non biaisé car 64 divise 256) et utilisez le résultat comme index dans votre tableau chars
.
Avec un alphabet de longueur 64, vous obtenez 6 bits d'entropie par caractère (car 26 = 64). Ainsi, avec 13 caractères, vous obtenez 78 bits d'entropie. En fin de compte, ce n’est pas toujours fort, mais déjà très fort (il pourrait être défait avec un budget qui se compterait en mois et en milliards de dollars, pas seulement en millions).
XKCD explique parfaitement pourquoi ce que vous pensez sont des mots de passe forts ne sont pas.
À quiconque comprend la théorie de l'information et la sécurité et est dans un argument exaspérant avec quelqu'un qui ne le fait pas (éventuellement, impliquant cas mélangé), je m'excuse sincèrement. - Randall Munroe
Et si vous ne comprenez pas le math derrière ce que cette illustration explique , n'essayez pas d'écrire quoi que ce soit qui devrait être cryptographiquement sécurisé, car ce ne sera pas le cas. Il suffit de poser la souris et de s’éloigner du clavier.
Il y a à peine deux jours, Kragen Javier Sitaker a publié un programme à cet effet sur http://lists.canonical.org/pipermail/kragen-hacks/2011-September/000527.html (disparu maintenant - essayez https : //github.com/jesterpm/bin/blob/master/mkpasswd )
Générez un mot de passe aléatoire, mémorisable: http://xkcd.com/936/
Exemple d'exécution:
kragen à inexorable: ~/devel/inexorable-misc $ ./mkpass.py 5 12 Votre mot de passe est "appris les dégâts sauvés les étapes résidentielles". Cela équivaut à une clé 60 bits.
Ce mot de passe prendrait 2.5e + 03 années CPU à craquer sur mon Celeron E1200 peu coûteux de 2008, en supposant une attaque hors ligne sur un hachage MS-Cache, qui est le pire algorithme de hachage de mot de passe couramment utilisé, légèrement pire que le simple MD5.
L’algorithme de hachage de mot de passe le plus courant de nos jours est le MD5 itéré de FreeBSD; craquer un tel hachage prendrait 5.2e + 06 années-processeurs.
Mais un GPU moderne peut craquer environ 250 fois plus vite, de sorte que le même MD5 itéré tomberait en 2e + 04 années GPU.
Ce GPU coûte environ 1,45 USD par jour pour fonctionner en 2011, donc déchiffrer le mot de passe coûterait environ 3e USD + 09.
J'ai commencé à utiliser un mot de passe généré de cette manière à la place d'un mot de passe aléatoire à 9 caractères ASCII imprimables, qui est tout aussi puissant. L'affirmation de Munroe selon laquelle ces mots de passe sont beaucoup plus faciles à mémoriser est correcte. Cependant, il reste un problème: comme il y a beaucoup moins de bits d'entropie par caractère (environ 1,7 au lieu de 6,6), le mot de passe est redondant, et des attaques telles que l'attaque par canal ssh (le Wagner et l'attaque Tian Herbivore, dont Bram Cohen m'avait parlé au Bagdad Café aux petites heures du matin un matin auparavant, et les attaques par enregistrement audio au clavier ont beaucoup plus de chances de saisir suffisamment d'informations pour rendre le mot de passe attaquable.
Mon contre-mesure à l’attaque Herbivore, qui fonctionne bien avec un mot de passe à 9 caractères mais qui est extrêmement ennuyeux avec mon nouveau mot de passe, consiste à taper le mot de passe avec un délai d’une demi-seconde entre les caractères, afin que le canal de synchronisation ne contienne pas beaucoup caractères réels utilisés. De plus, la longueur inférieure du mot de passe de 9 caractères donne de manière inhérente à l'approche Herbivore beaucoup moins d'informations à mâcher.
Parmi les autres contre-mesures possibles, vous pouvez utiliser le mode shell Emacs, qui vous invite localement à saisir le mot de passe lorsqu'il reconnaît une invite de mot de passe, puis envoie l'intégralité du mot de passe en une fois, puis en le copiant et en le collant ailleurs.
Comme vous vous en doutez, la saisie de ce mot de passe prend également un peu plus de temps: environ 6 secondes au lieu de 3 secondes environ.
#!/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))
implémentation de la solution @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
Une autre implémentation de la méthode 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()
Exemple de sortie:
$ ./randompassword.py
affluent afford scarlets twines
$ ./randompassword.py
speedboat ellipse further staffer
Je sais que cette question a été postée en 2011, mais pour ceux qui s'y intéressent maintenant en 2014 et au-delà, j'ai une chose à dire: RESISTER À L'INCITRANT DE RÉINVENTER LA ROUE.
Dans ces situations, le mieux est de rechercher un logiciel open source, par exemple, de limiter votre recherche aux résultats de github. De loin la meilleure chose que j'ai trouvée:
Vous ne pouvez pas faire confiance au générateur de nombres pseudo aléatoires de python lors de la génération d'un mot de passe. Ce n'est pas nécessairement cryptographiquement aléatoire. Vous amorcez le générateur de nombres pseudo aléatoires à partir de os.urandom
, ce qui est un bon début. Mais vous dépendez ensuite du générateur de python.
Un meilleur choix serait la classe random.SystemRandom()
qui prend des nombres aléatoires de la même source que urandom
. Selon la documentation python, cela devrait suffire à une utilisation cryptographique. La classe SystemRandom
vous donne tout ce que la classe aléatoire principale fait, mais vous n'avez pas à vous soucier de la pseudo-aléatoire.
Exemple de code utilisant random.SystemRandom (pour 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))
Remarque: votre kilométrage peut varier - la documentation Python indique que la disponibilité aléatoire.SystemRandom varie en fonction du système d'exploitation.
Compte tenu de votre commentaire,
Je dois juste être capable de générer des mots de passe plus sûrs que ceux que je trouverais dans ma tête.
il semble que vous souhaitiez utiliser votre programme pour générer des mots de passe, plutôt que de l'écrire comme un exercice. Il est préférable d’utiliser une implémentation existante, car si vous faites une erreur, la sortie risque d’être compromise. En savoir plus sur attaques de générateurs de nombres aléatoires ; en particulier, un bogue RNG bien connu dans Debian a révélé les clés privées SSL des personnes.
Alors, utilisez plutôt pwgen
. Il propose plusieurs options, que vous devez choisir en fonction de l'utilisation prévue des mots de passe.
Implémentation de la solution @Thomas Pornin: (ne peut commenter la réponse inexacte de @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()
Bien sûr, cela peut être amélioré, mais c’est ce que j’utilise.
Construit ma propre réponse CLI au sujet traité (code source complet à l'URL suivante):
http://0netenv.blogspot.com/2016/08/password-generator-with-argparse.html
A écrit un générateur de mot de passe en utilisant argparse . J'espère que cela aidera quelqu'un (soit en construisant un générateur de mot de passe, soit en utilisant argparse)!
De toute façon, c'était amusant à construire!
$ ./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
Voici le code:
#!/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
C'est facile :)
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
et le faire:
print codegenerator()
Merci http://xkcd.com/936/
Cela fonctionne. C'est parfaitement bien. Si vous aviez des règles supplémentaires, telles que l'exclusion des mots du dictionnaire, vous souhaiterez peut-être également inclure ces filtres, mais la probabilité de générer de manière aléatoire un mot du dictionnaire avec cette configuration est extrêmement petite.
J'adore la linguistique. Dans mon approche, je crée des pseudo mots mémorables avec un niveau d'entropie élevé en alternant consonnes et voyelles.
Code 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'
Les inconvénients:
Votre implémentation pose quelques problèmes:
random.seed = (os.urandom(1024))
Cela ne sème pas le générateur de nombres aléatoires; il remplace la fonction seed
par une chaîne d'octets. Vous devez appeler seed
, par exemple, random.seed(…)
.
print ''.join(random.choice(chars) for i in range(length))
Le paramètre par défaut de Python PRNG est un Mersenne Twister, qui n'est pas un PRNG cryptographiquement puissant. Je me méfie donc de l'utiliser à des fins cryptographiques. Le module random
inclut random.SystemRandom
, qui, sur au moins la plupart des systèmes * nix, doit utiliser un fichier CSPRNG. Toutefois ,
random.choice(chars)
… Est mis en œuvre comme…
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
… Dans Python 2 . Malheureusement, self.random
ici est une fonction C, donc cela devient difficile à voir; L'odeur de code ici est que ce code ne choisit presque certainement pas de manière uniforme. Le code a complètement changé dans Python 3 et assure beaucoup mieux l’uniformité. La documentation Python 3 pour la note randrange
,
Modifié dans la version 3.2:
randrange()
est plus sophistiqué quant à la production de valeurs également distribuées. Autrefois, il utilisait un style commeint(random()*n)
qui pouvait produire des distributions légèrement inégales.
randrange
et choice
appellent tous deux la même méthode (_randbelow
) sous le capot.
En Python 3, choice
va bien; en Python 2, il ne se rapproche que de près d'une distribution uniforme, mais ne la garantit pas. Puisqu'il s'agit d'une crypto, je m'appuie sur le côté "ne prenez aucune chance" de la clôture et souhaite bénéficier de cette garantie.
J'ai récemment commencé à apprendre python et c'est quelque chose que j'ai écrit aujourd'hui. J'espère que cela t'aides.
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())
Un peu hors sujet, mais j'ai fait cela en utilisant aussi TKinter. J'espère que cela peut aider:
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()
Voici une autre implémentation (python 2; nécessiterait quelques réécritures mineures pour le faire fonctionner en 3) qui est beaucoup plus rapide que celle d'OJW, qui semble parcourir le dictionnaire pour chaque mot, malgré le commentaire/l'implication contraire. Chronométrage du script OJW sur ma machine, avec un SSD 80 000 IOP:
real 0m3.264s
user 0m1.768s
sys 0m1.444s
Le script suivant charge l'ensemble du dictionnaire dans une liste, puis sélectionne des mots en fonction d'une sélection aléatoire de la valeur d'index, à l'aide de la regex d'OJW pour le filtrage.
Cela génère également 10 jeux de phrase secrète, permet de passer des paramètres de ligne de commande pour ajuster le nombre de mots, et ajoute un remplissage de nombre et de symboles (longueur également réglable).
Exemples de temps pour ce script:
real 0m0.289s
user 0m0.176s
sys 0m0.108s
Utilisation: xkcdpass-mod.py 2 4 (par exemple, ce sont les valeurs par défaut).
Il permet d’imprimer facilement des espaces dans la sortie, même si je n’ai presque jamais rencontré de service en ligne permettant de les utiliser. Par conséquent, je ne les ignorerais pas. Cela pourrait définitivement être corrigé avec argparse ou getopt et permettre des commutations pour inclure ou non des espaces, y compris/exclure des symboles, des majuscules, etc., ainsi que quelques modifications supplémentaires, mais je n'y suis pas encore parvenu. Alors, sans plus tarder:
#!/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])
Exemple de sortie:
[✗]─[user@machine]─[~/bin]
└──╼ xkcdpass-mod.py
item 1
digress basketball )%^)
item 2
graves giant &118
item 3
impelled maniacs ^@%1
Et optez pour le "bon agrafage de la batterie du cheval" (CHBS), sans rembourrage:
┌─[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
Selon https://www.grc.com/haystack.htm , à toutes fins pratiques, en supposant que 100 000 milliards de suppositions par seconde (soit 100 TH/s), la version plus courte prendrait environ 50 à 60 millions de siècles. à craquer; le total des CHBS = 1,24 milliards de milliards de milliards de siècles; ajouter 15,5 milliards de milliards de milliards de milliards de siècles.
Même en enrôlant l’ensemble du réseau minier Bitcoin (environ 2 500 TH/s au moment de la rédaction de ce document), la version courte prendrait probablement entre 250 et 300 millions d’années, ce qui est probablement suffisamment sécurisé pour la plupart des applications.
Ceci est un petit programme simple destiné aux personnes qui ne peuvent pas trouver un mot de passe sécurisé pour leurs propres comptes publics.
Exécutez simplement le programme sur une console de commande et transmettez une série de lettres qui vous semble familière. Une séquence de symboles basée sur ce que vous avez inséré sera alors générée.
bien entendu, le programme ne prend pas en charge la génération de séquences multiples.
Vous pouvez télécharger le code depuis mon compte github: 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 nous a permis de coder des données binaires en mode lisible/inscriptible sans perte de données.
import os
random_bytes=os.urandom(12)
secret=random_bytes.encode("base64")
Ma solution basée sur la réponse de @Thomas Pornin (Mise à jour)
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())
Cette fonction renvoie un mot de passe aléatoire (sans numéro ni symbole au début du mot de passe).
Voici mon générateur de mot de passe aléatoire après avoir recherché ce sujet:
`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)`
Cela générera un mot de passe aléatoire avec 1 lettre majuscule aléatoire, 3 lettres minuscules aléatoires, 3 chiffres aléatoires et 1 caractère spécial aléatoire - vous pouvez ajuster ce paramètre si nécessaire. Ensuite, il combine chaque caractère aléatoire et crée un ordre aléatoire. Je ne sais pas si cela est considéré comme "de haute qualité", mais le travail est fait.