112 votes

Générer un mot de passe en Python

Je voudrais générer des mots de passe alphanumériques en Python. Voici quelques façons possibles :

import string
from random import sample, choice
chars = string.ascii_letters + string.digits
length = 8
''.join(sample(chars, length))                 # première méthode
''.join(choice(chars) for i in range(length))  # deuxième méthode

Mais je n'aime pas la première méthode car seuls des caractères uniques sont sélectionnés et vous ne pouvez pas générer des mots de passe où length > len(chars) et je n'aime pas la deuxième méthode car nous avons une variable i inutilisée. Y a-t-il d'autres bonnes options ?

239voto

gerrit Points 1588

Sur Python 3.6 et plus, vous devriez utiliser le module secrets pour générer des mots de passe sûrs sur le plan cryptographique. Adapté de la documentation :

import secrets
import string
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(20))  # pour un mot de passe de 20 caractères

Pour plus d'informations sur les recettes et les meilleures pratiques, consultez cette section sur les recettes dans la documentation Python. Vous pouvez également envisager d'ajouter string.punctuation.

38voto

Ben Mosher Points 5005

Pour les amateurs de crypto-PRNG :

def generate_temp_password(length):
    if not isinstance(length, int) or length < 8:
        raise ValueError("le mot de passe temporaire doit avoir une longueur positive")

    chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
    from os import urandom

    # Python 2 original (urandom retourne une str)
    # return "".join(chars[ord(c) % len(chars)] for c in urandom(length))

    # Python 3 (urandom retourne des bytes)
    return "".join(chars[c % len(chars)] for c in urandom(length))

Notez que pour une distribution uniforme, la longueur de la chaîne chars devrait être un diviseur intégral de 128 ; sinon, vous aurez besoin d'une façon différente de choisir de manière uniforme dans l'espace.

33voto

np8 Points 1538

Deux recettes utilisant le module secrets (python 3.6+)

1. secrets.token_urlsafe

C'est beaucoup plus rapide que la réponse acceptée. (voir les temps ci-dessous)

import secrets
mot_de_passe = secrets.token_urlsafe(32)

Exemple de sortie :

4EPn9Z7RE3l6jtCxEy7CPhia2EnYDEkE6N1O3-WnntU

L'argument pour token_urlsafe est le nombre d'octets. En moyenne, un octet équivaut à 1,3 caractères (encodage en base64).

2. Imposer un certain nombre de chiffres/caractères en majuscules, etc

C'est une copie légèrement modifiée de la documentation de secrets. Avec cela, vous avez un contrôle plus précis sur l'apparence des mots de passe générés. Bien sûr, ce n'est pas une option rapide si vous avez besoin de générer un grand nombre de mots de passe.

  • Longueur forcée à 20 caractères
  • Forcer au moins 4 caractères en minuscules
  • Forcer au moins 4 caractères en majuscules
  • Forcer au moins 4 chiffres
  • Des caractères spéciaux peuvent être ajoutés à l'alphabet. Dans cet exemple, seuls - et _ sont ajoutés.

    import string import secrets alphabet = string.asciiletters + string.digits + '-' while True: mot_de_passe = ''.join(secrets.choice(alphabet) for i in range(20)) if (sum(c.islower() for c in mot_de_passe) >=4 and sum(c.isupper() for c in mot_de_passe) >=4 and sum(c.isdigit() for c in mot_de_passe) >=4): break

Exemple de sortie :

HlxTm2fcFE54JA1I_Yp5

3. "Je n'ai pas besoin de contrôle plus précis"

Si vous privilégiez la vitesse, vous pouvez également omettre la boucle while. Dans ce cas, cela se simplifie en fait à la réponse de gerrit (mais vous perdez alors le contrôle plus précis) :

import string
import secrets
alphabet = string.ascii_letters + string.digits + '-_'
mot_de_passe = ''.join(secrets.choice(alphabet) for i in range(20))

Comparaison de vitesse

1. secrets.token_urlsafe

1.62 µs ± 96.6 ns par boucle (moyenne ± écart-type de 7 exécutions, 1000000 boucles chacune)

2. Imposer un certain nombre de chiffres/caractères en majuscules, etc

107 µs ± 11.9 µs par boucle (moyenne ± écart-type de 7 exécutions, 10000 boucles chacune)

3. "Je n'ai pas besoin de contrôle plus précis"

77.2 µs ± 9.31 µs par boucle (moyenne ± écart-type de 7 exécutions, 10000 boucles chacune)

Configuration de la comparaison de vitesse : python 3.8.5 64 bits sur Win10, 43 caractères dans chaque mot de passe (=32 octets pour token_urlsafe).

12voto

SilentGhost Points 79627

AVERTISSEMENT cette réponse doit être ignorée en raison de problèmes de sécurité critiques !

L'option n°2 semble tout à fait raisonnable, sauf que vous pourriez ajouter quelques améliorations :

''.join(choice(chars) for _ in range(length))          # en py2k utilisez xrange

_ est une variable conventionnelle signifiant "Je ne m'intéresse pas à ce qui est à l'intérieur". Et vous n'avez pas besoin de la compréhension de liste là, une expression de générateur fonctionne très bien pour str.join. Il n'est pas clair non plus ce que signifie "lent", s'il s'agit de la seule façon correcte.

6voto

Tim Ludwinski Points 457

Je pense que cela fera l'affaire. random.SystemRandom utilise la même fonction de cryptage sous-jacente que os.urandom, mais il utilise l'interface familière de random. Cette fonction ne sera pas sujette à la bizarrerie des 128 octets comme dans la réponse de Ben.

import random
import string

def gen_random_string(char_set, length):
    if not hasattr(gen_random_string, "rng"):
        gen_random_string.rng = random.SystemRandom() # Créer une variable statique
    return ''.join([ gen_random_string.rng.choice(char_set) for _ in xrange(length) ])

password_charset = string.ascii_letters + string.digits
gen_random_string(password_charset, 32)

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