Generate password in python

89,503

Solution 1

You should use the secrets module to generate cryptographically safe passwords, which is available starting in Python 3.6. Adapted from the documentation:

import secrets
import string
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(20))  # for a 20-character password

For more information on recipes and best practices, see this section on recipes in the Python documentation. You can also consider adding string.punctuation or even just using string.printable for a wider set of characters.

Solution 2

For the crypto-PRNG folks out there:

def generate_temp_password(length):
    if not isinstance(length, int) or length < 8:
        raise ValueError("temp password must have positive length")

    chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
    from os import urandom

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

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

Note that for an even distribution, the chars string length ought to be an integral divisor of 128; otherwise, you'll need a different way to choose uniformly from the space.

Solution 3

Two recipes using the builtin secrets (python 3.6+)

1. secrets.token_urlsafe

This is much faster than the accepted answer. (see timings below)

import secrets
password = secrets.token_urlsafe(32)

Example output:

4EPn9Z7RE3l6jtCxEy7CPhia2EnYDEkE6N1O3-WnntU

The argument for token_urlsafe is number of bytes. On average, one byte is 1.3 characters (base64 encoded).

2. Enforce amount of digits/upper characters etc

This is slighly modified copy from the docs of secrets. With this you have more fine grained control on how to generated passwords have to look. Of course, this is not fast option if you need to generate a lot of passwords.

  • Forcing length to be 20 characters
  • Forcing at least 4 lower case character
  • Forcing at least 4 upper case characters
  • Forcing at least 4 digits
  • Special characters can be added to alphabet. In this example, there are just - and _ added.
import string
import secrets
alphabet = string.ascii_letters + string.digits + '-_'
while True:
    password = ''.join(secrets.choice(alphabet) for i in range(20))
    if (sum(c.islower() for c in password) >=4
            and sum(c.isupper() for c in password) >=4
            and sum(c.isdigit() for c in password) >=4):
        break

Example output:

HlxTm2fcFE54JA1I_Yp5

3. "I don't need the finer-grained control"

If considered speed, you can also drop the while-loop. In this case, it actually simplifies to gerrit's answer (but then you loose the finer-grained control):

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

Speed comparison

1. secrets.token_urlsafe

1.62 µs ± 96.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

2. Enforce amount of digits/upper characters etc

107 µs ± 11.9 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

3. "I don't need the finer-grained control"

77.2 µs ± 9.31 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Speed comparison setup: python 3.8.5 64-bit on Win10, 43 characters in each password (=32 bytes for token_urlsafe).

Solution 4

WARNING this answer should be ignored due to critical security issues!

Option #2 seems quite reasonable except you could add a couple of improvements:

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

_ is a conventional "I don't care what is in there" variable. And you don't need list comprehension there, generator expression works just fine for str.join. It is also not clear what "slow" means, if it is the only correct way.

Solution 5

I think this'll do the trick. random.SystemRandom uses the same underlying crypto random function as os.urandom but it uses the familiar random interface. This function won't be subject to the weird 128 byte thing as in Ben's answer.

import random
import string

def gen_random_string(char_set, length):
    if not hasattr(gen_random_string, "rng"):
        gen_random_string.rng = random.SystemRandom() # Create a static variable
    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)
Share:
89,503

Related videos on Youtube

HardQuestions
Author by

HardQuestions

Work as programmer and systems administrator.

Updated on February 16, 2022

Comments

  • HardQuestions
    HardQuestions about 2 years

    I'dl like to generate some alphanumeric passwords in python. Some possible ways are:

    import string
    from random import sample, choice
    chars = string.letters + string.digits
    length = 8
    ''.join(sample(chars,length)) # way 1
    ''.join([choice(chars) for i in range(length)]) # way 2
    

    But I don't like both because:

    • way 1 only unique chars selected and you can't generate passwords where length > len(chars)
    • way 2 we have i variable unused and I can't find good way how to avoid that

    So, any other good options?

    P.S. So here we are with some testing with timeit for 100000 iterations:

    ''.join(sample(chars,length)) # way 1; 2.5 seconds
    ''.join([choice(chars) for i in range(length)]) # way 2; 1.8 seconds (optimizer helps?)
    ''.join(choice(chars) for _ in range(length)) # way 3; 1.8 seconds
    ''.join(choice(chars) for _ in xrange(length)) # way 4; 1.73 seconds
    ''.join(map(lambda x: random.choice(chars), range(length))) # way 5; 2.27 seconds
    

    So, the winner is ''.join(choice(chars) for _ in xrange(length)).

    • DisplacedAussie
      DisplacedAussie over 13 years
      There's nothing really wrong with the second option. Is it too slow? Do you need it to be faster? Are you running out of memory?
    • SilentGhost
      SilentGhost over 13 years
      don't use list comprehension for the 2nd option. use generator expression.
    • HardQuestions
      HardQuestions over 13 years
      Unused 'i' bother my mind and IDE. ;)
    • HardQuestions
      HardQuestions over 13 years
      @SilengGhost: Nice trick with _ If you make it as answer I'll accept it.
    • Jocelyn delalande
      Jocelyn delalande over 9 years
      Note that for py3 you would have to use string.ascii_letters instead of string.letters so to make portable code, best is to list the chars you allow in a str instead of using string module constants.
    • livibetter
      livibetter over 8 years
      Just quick FYI, the draft PEP 0506 -- Adding A Secrets Module To The Standard Library just came out and it specifically links to this question and an answer.
    • some bits flipped
      some bits flipped almost 3 years
      you should NOT use the random module, it's pseudo-randomness is fine for testing but INCREDIBLY weak for this purpose. Use the built-in secrets module instead
  • Fasaxc
    Fasaxc almost 12 years
    You should use secure random number generators for password generation or your passwords may be easily compromised. The default python RNG is not a secure one. "Python uses the Mersenne Twister as the core generator. ... The Mersenne Twister ... is completely unsuitable for cryptographic purposes." -docs.python.org/library/random.html
  • Greg Glockner
    Greg Glockner about 9 years
    You can eliminate the list in the last line via a generator expression: return "".join(chars[ord(c) % len(chars)] for c in urandom(length))
  • Emil Stenström
    Emil Stenström over 8 years
    This is the best answer, but you should clearify the answer so that anyone finding this via Google understands why it's the best one.
  • Scott Greenup
    Scott Greenup over 6 years
    I would like to backup Fasaxc on this. Please look at @livibetter's comment, use the secrets module from PEP0506. As livibetter stated, this article is referenced as a bad example. I know this is an old question that I am commenting on, but it's still one of the top search results. I'd recommend someone change the accepted answer to gerrit's.
  • Josiah Yoder
    Josiah Yoder over 6 years
    crypto-PRNG stands for "cryptograhically-secure Pseudorandom number generator". This answer is one of the best because it generates secure passwords. The default pseudorandom number generator in the random module is not secure.
  • Eli Korvigo
    Eli Korvigo over 6 years
    Why would you want to set rng as an attribute?
  • Tim Ludwinski
    Tim Ludwinski over 6 years
    This is so you don't have to initialize random.SystemRandom() each time.
  • Alvin
    Alvin almost 6 years
    throws errors like invalid literal for int() with base 10: '\xba' -- so need some fault tolerance added
  • gardarh
    gardarh almost 6 years
    Huh, I can't really understand how I missed that. Updated answer with fixed code (added struct.unpack()) and also cleaned up index calculation.
  • Ben Mosher
    Ben Mosher over 5 years
    FWIW, after years of service, this code generated an obscene password. here is another post about the perils of alphanumeric password/id generation: softwareengineering.stackexchange.com/q/143405/32537
  • Tim Ludwinski
    Tim Ludwinski over 3 years
    One thing to note, this solution isn't any less secure (in the current version of python) than the accepted version that uses secrets.choice. secrets.choice is an alias of random.SystemRandom().choice, although I would recommend the secrets module for newer programs. (This post was created when python 2 was still a thing and the secrets module wasn't universally available).
  • user5359531
    user5359531 over 3 years
    worth noting that this appears to require Python 3.6+
  • gerrit
    gerrit over 3 years
    @user5359531 That note was here for years, but I deleted that note because Python 3.5 is no longer supported since September 2020, and I don't want to maintain answers for unmaintained Python versions.
  • user5359531
    user5359531 over 3 years
    that is kinda a weird position to take, legacy versions of Python are still in existence on systems all over the world regardless of the EOL. And plenty of systems will have multiple versions of Python installed for compatibility.
  • gerrit
    gerrit over 3 years
    @user5359531 On another look it was Boris and not me who edited it out of the answer, but I agree with the edit. Anyway, the first sentence of the answer still notes that is available since Python 3.6 (just not as prominently as before).
  • Vinay Kumar
    Vinay Kumar over 3 years
    TypeError: ord() expected string of length 1, but int found I am getting this error when running this code, don't know why people are saying this is good.
  • Ben Mosher
    Ben Mosher over 3 years
    @VinayKumarShukla good catch, this was written originally for Python 2. added a Python 3 version.
  • np8
    np8 almost 3 years
    Should this be a comment to some answer?
  • Jason Short
    Jason Short almost 2 years
    lenght <> length