89 votes

Générateur de mot de passe aléatoire simple et de haute qualité

Je suis intéressé par la création d'un générateur de mots de passe aléatoires très simple et de haute qualité (cryptographique). Existe-t-il un meilleur moyen de le faire ?

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

7 votes

Ces mots de passe sont-ils destinés aux humains ou aux machines ?

21 votes

@JarrodRoberson Affirmer que "aléatoire != cryptographie" est tout simplement flagrant en soi, puisque la cryptographie moderne est construite sur l'aléatoire. Tous les aléas ne sont pas égaux (par exemple, un mot de passe choisi au hasard parmi "password" et "passwords" n'est évidemment pas sûr), mais au fond, cryptographie = = aléa.

4 votes

N'est-ce pas ? random.seed une méthode, donc random.seed = 'random_string' détruit essentiellement cette méthode et ne fait rien ? Voulez-vous dire random.seed('random_string') ?

49voto

Thomas Pornin Points 36984

La difficulté avec les mots de passe est de les rendre suffisamment forts tout en étant capable de s'en souvenir. Si le mot de passe n'est pas destiné à être mémorisé par un être humain, alors ce n'est pas vraiment un mot de passe.

Vous utilisez l'outil Python os.urandom() C'est bien. Pour tout objectif pratique (même la cryptographie), la sortie de os.urandom() est indiscernable de la vraie alea. Ensuite, vous l'utilisez comme semence dans random Ce dernier est un PRNG non cryptographique et sa sortie peut présenter une structure qui ne sera pas enregistrée par un outil de mesure statistique, mais qui pourrait être exploitée par un attaquant intelligent. Vous devriez travailler avec os.urandom() depuis le début. Pour simplifier les choses, choisissez un alphabet de 64 caractères, par exemple des lettres (majuscules et minuscules), des chiffres et deux caractères de ponctuation supplémentaires (tels que "+" et "/"). Ensuite, pour chaque caractère du mot de passe, prenez un octet de os.urandom() réduisez la valeur modulo 64 (ceci n'est pas biaisé car 64 divise 256) et utilisez le résultat comme index dans votre fichier chars le tableau.

Avec un alphabet de longueur 64, vous obtenez 6 bits d'entropie par caractère (parce que 2 6 \= 64). Ainsi, avec 13 caractères, on obtient 78 bits d'entropie. Ce n'est pas finalement fort dans tous les cas, mais déjà très fort (on pourrait le vaincre avec un budget qui se comptera en mois et en milliards de dollars, et non en simples millions).

0 votes

Attendez une minute... Ca peut sembler être une question stupide, mais os.urandom() type de retour bytes alors comment puis-je réduire ce mod 64 ?

2 votes

Demandez un octet, puis obtenez la valeur de l'octet, qui est un nombre entier compris entre 0 et 255.

1 votes

Mais c'est assez difficile de se souvenir de 13 caractères aléatoires. C'est en fait le point de vue de Randall dans la célèbre bande dessinée XKCD. Je suggérerais github.com/redacted/XKCD-password-generator en utilisant son option --acrostic pour obtenir un motif que vous trouvez encore plus facile à retenir.

48voto

Jarrod Roberson Points 32263

XKCD a une bonne explication du pourquoi ce que vous pensez sont des mots de passe forts ne sont pas .

http://xkcd.com/936/

Pour quiconque comprend la théorie de l'information et la sécurité et se trouve dans une une discussion exaspérante avec quelqu'un qui ne le comprend pas (impliquant peut-être un cas mixte), je m'excuse sincèrement. - Randall Munroe

Et si vous ne comprenez pas le les mathématiques derrière ce que cette illustration explique Si vous n'êtes pas en mesure d'écrire quelque chose qui soit cryptographiquement sûr, n'essayez pas de le faire, car ce ne sera pas le cas. Posez simplement la souris et éloignez-vous du clavier.

4 votes

Pour ce que ça vaut, il y a un billet sur la sécurité informatique SE à propos de cette BD que Jeff a récemment utilisée comme exemple de bonne question.

1 votes

Cette BD XKCD ne vient pas de rien. Elle est basée sur ce qui est vrai... Ce que je dois dire, c'est que l'allusion de la bande dessinée était : "A tous ceux qui comprennent la théorie de l'information et la sécurité et qui se trouvent dans une discussion exaspérante avec quelqu'un qui ne les comprend pas (impliquant éventuellement des cas mixtes), je m'excuse sincèrement." explainxkcd.com/2011/08/10/passeword-strength De plus, LifeHacker a écrit un article à ce sujet. lifehacker.com/5830355/…

1 votes

C'est ce dont Randal parlait quand il a fait le commic : Pourquoi le mot de passe "this is fun" est 10 fois plus sûr que "J4fS!2".

13voto

rm. Points 2237

Il y a deux jours à peine, Kragen Javier Sitaker a mis en ligne un programme permettant de faire cela à l'adresse suivante http://lists.canonical.org/pipermail/kragen-hacks/2011-September/000527.html (disparu maintenant - essayez https://github.com/jesterpm/bin/blob/master/mkpasswd )

Générer un mot de passe aléatoire et mémorisable : http://xkcd.com/936/

Exemple d'exécution :

kragen at inexorable:~/devel/inexorable-misc$ ./mkpass.py 5 12 Votre mot de passe est "learned damage saved residential stages". C'est l'équivalent d'une clé de 60 bits.

Il faudrait 2,5e+03 années-processeur pour craquer ce mot de passe sur mon Celeron E1200 bon marché 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 ; le craquage d'un tel hachage prendrait 5.2e+06 années-processeur.

Mais un GPU moderne peut craquer environ 250 fois plus vite, de sorte que le même MD5 itéré tomberait en 2e+04 GPU-années.

Le fonctionnement de ce GPU coûte environ 1,45 dollar par jour en 2011, et le craquage du mot de passe coûterait donc environ 3e+09 dollars.

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 fort. L'affirmation de Munroe selon laquelle ces mots de passe sont beaucoup plus faciles à mémoriser est correcte. Cependant, il y a toujours un problème : comme il y a beaucoup moins de bits d'entropie par caractère (environ 1,7 au lieu de 6,6), il y a beaucoup de redondance dans le mot de passe, et donc des attaques telles que l'attaque ssh par canal temporel (l'attaque Song, Wagner et Tian Herbivore, que j'ai apprise de Bram Cohen au Bagdad Café au petit matin, il y a des années) et les attaques par enregistrement audio du clavier ont beaucoup plus de chances de capturer suffisamment d'informations pour rendre le mot de passe attaquable.

Ma contre-mesure à l'attaque Herbivore, qui fonctionne bien avec un mot de passe à 9 caractères mais est extrêmement gênante avec mon nouveau mot de passe, est de taper le mot de passe avec un délai d'une demi-seconde entre les caractères, de sorte que le canal de synchronisation ne porte pas beaucoup d'informations sur les caractères réellement utilisés. De plus, la longueur plus faible du mot de passe à 9 caractères donne intrinsèquement à l'approche Herbivore beaucoup moins d'informations à mâcher.

Parmi les autres contre-mesures possibles, citons l'utilisation du mode shell d'Emacs, qui vous demande localement le mot de passe lorsqu'il reconnaît une demande de mot de passe, puis envoie l'intégralité du mot de passe en une seule fois, et le copier-coller du mot de passe depuis un autre endroit.

Comme on peut s'y attendre, ce mot de passe est également un peu plus long à taper : environ 6 secondes au lieu de 3 secondes.

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

import random, itertools, os, sys

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if __name__ == '__main__':
    sys.exit(main(sys.argv))

1 votes

Pourriez-vous mettre generate_password() (avec random.SystemRandom() ) en haut de votre réponse ? Cela peut aider les personnes qui ont atterri ici en cherchant : "générateur de mot de passe python"

11voto

yossi Points 3655

Mise en œuvre de la solution de @Thomas Pornin

import M2Crypto
import string

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

4 votes

Vous pouvez simplement utiliser os.urandom(1) (cryptographiquement fort) et supprimer la dépendance de M2Crypto.

2 votes

Utilisation de % len(chars) de cette façon a un léger biais vers les 8 premiers caractères de l'alphabet. chars . Chacune de ces lettres apparaîtrait 1,95 % du temps, contre 1,56 % pour les autres caractères.

7voto

OJW Points 1191

Une autre mise en œuvre 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(__name__ == "__main__"):
  print genPassword()

Exemple de sortie :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X