Generate password in python
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)
Related videos on Youtube
Comments
-
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 over 13 yearsThere'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 over 13 yearsdon't use list comprehension for the 2nd option. use generator expression.
-
HardQuestions over 13 yearsUnused 'i' bother my mind and IDE. ;)
-
HardQuestions over 13 years@SilengGhost: Nice trick with _ If you make it as answer I'll accept it.
-
Jocelyn delalande over 9 yearsNote that for py3 you would have to use
string.ascii_letters
instead ofstring.letters
so to make portable code, best is to list the chars you allow in astr
instead of using string module constants. -
livibetter over 8 yearsJust 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 almost 3 yearsyou should NOT use the
random
module, it's pseudo-randomness is fine for testing but INCREDIBLY weak for this purpose. Use the built-insecrets
module instead
-
Fasaxc almost 12 yearsYou 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 about 9 yearsYou 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 over 8 yearsThis 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 over 6 yearsI 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 over 6 yearscrypto-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 over 6 yearsWhy would you want to set
rng
as an attribute? -
Tim Ludwinski over 6 yearsThis is so you don't have to initialize
random.SystemRandom()
each time. -
Alvin almost 6 yearsthrows errors like
invalid literal for int() with base 10: '\xba'
-- so need some fault tolerance added -
gardarh almost 6 yearsHuh, 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 over 5 yearsFWIW, 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 over 3 yearsOne 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 ofrandom.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 over 3 yearsworth noting that this appears to require Python 3.6+
-
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 over 3 yearsthat 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 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 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 over 3 years@VinayKumarShukla good catch, this was written originally for Python 2. added a Python 3 version.
-
np8 almost 3 yearsShould this be a comment to some answer?
-
Jason Short almost 2 yearslenght <> length