0

I need to generate random passwords for my company's 200k+ customers.

The password complexity requirement is a common one:

  1. length > 8
  2. contains at least one upper case character
  3. contains at least one lower case character
  4. contains at least one number
  5. contains at least one symbols (e.g. @#$%)

Here is the python 3.8 code I used to generate a random password string following the guides on Google search result(like this and this):

import secrets
import string

def getRandomPasswordString(length):
    
    alphabet = string.ascii_letters + string.digits + string.punctuation
    password = ''.join(secrets.choice(alphabet) for i in range(length))

    return password

This works fine for most of the time but for some rare cases that the password generated does not comply with the complexity requirement like below:

=Y[&PE-XXP[//F, (missing lower case letter)

^f~+""uwan]ny)b (missing upper case letter)

AQvoMApuNFyRkJd (missing symbols and numbers)

I know that I can do something like this to ensure each types of character are chosen:

import secrets
import string

def getRandomPasswordString(length):
    
    alphabet = string.ascii_letters + string.digits + string.punctuation

    password = secrets.choice(string.ascii_uppercase) + \
            secrets.choice(string.ascii_lowercase) + \
            secrets.choice(string.digits) + \
            secrets.choice(string.punctuation) + \
            ''.join(secrets.choice(alphabet) for i in range(length-4))

    return password

This works ok, but I am not sure if imposing some password patterns in the first 4 characters will cause any problem or not(i.e. the pattern is UPPERCASE > LOWERCASE > DIGIT > SYMBOLS)

Therefore, I would like to explore if there is any clean, one-line/shorter solution for generating the required passwords.

Many thanks in advance

ray
  • 11,310
  • 7
  • 18
  • 42
  • Why are _you_ generating passwords for your clients? You should never know your users' passwords. Password sharing in all forms is poor security. Instead, enforce a certain password entropy and require _your users_ to generate good passwords. – ChrisGPT was on strike Dec 19 '20 at 04:00
  • We are putting customer's accounts into Azure AD through Microsoft Graph API. We don't actually store the password we generated. We just want to put something in the password field as it is required by Azure AD. Users will be prompted to reset their password once they sign in their account :) – ray Dec 19 '20 at 04:03
  • Your second method reduces the entropy too much. The easiest and probably most efficient solution is to generate the password in a loop, and only exit the loop when the password meets the requirements. For passwords >= 8 characters the average number of iterations of this loop is less than 2. – President James K. Polk Dec 19 '20 at 12:37
  • Hi @PresidentJamesK.Polk, your advice looks promising :) I am now assigning length = 15 password. Will the average number of iterations greatly increase? :) – ray Dec 21 '20 at 03:59
  • As the password gets longer the average number of iterations *decreases*. – President James K. Polk Dec 21 '20 at 04:21

4 Answers4

2

I prefer to keep my generated passwords a little more complex. You can change the variation of amount of upper, lower, punctuation or numbers in the if statement:

    import string
    import secrets


    opts = string.ascii_letters + string.digits + string.punctuation
    #Generate a random password with at least two lower case, two upper case, one digit, and two symbols
    while True:
        psswd = ''.join(secrets.choice(opts) for i in range(12))
        if (sum(c.islower() for c in psswd) >=2 and
            sum(c.isupper() for c in psswd) >=2 and
            sum(c in string.punctuation for c in psswd) >=2 and
            any(c.isdigit() for c in psswd) ):
            break
    return psswd
pshute
  • 61
  • 7
1

simply shuffle the resulting password at the end by adding this:

password = "".join(random.sample(password,len(password)))

This way you meet the requirements without creating a pattern.

or you could shuffle the requirements and write the function like this:

from random  import sample
from secrets import choice
from string  import *

def getRandomPassword(length):
    alphabet      = ascii_letters + digits + punctuation
    requirements  = [ascii_uppercase,        # at least one uppercase letter
                     ascii_lowercase,        # at least one lowercase letter
                     digits,                 # at least one digit
                     punctuation,            # at least one symbol
                     *(length-4)*[alphabet]] # rest: letters digits and symbols
    return "".join(choice(req) for req in sample(requirements,length)) 
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • This answer looks clean and simple enough :) – ray Dec 21 '20 at 04:01
  • Your answer should use `secrets` rather than `random`; the question post is correct in this respect. See: https://stackoverflow.com/questions/61399901/random-password-generation-with-digit-letter-symbol/61458972#61458972 – Peter O. Dec 21 '20 at 15:22
  • Agreed but secrets does not have a sample function so I had to mix both. The character choice will remain as secure as before and the less secure shuffling will not expose the password too much. – Alain T. Dec 21 '20 at 15:29
  • The `random.SystemRandom()` and `secrets.SystemRandom()` objects contain the `choices` methods, not just the global `random` object. Both use a cryptographic RNG unlike the global `random` object. – Peter O. Dec 24 '20 at 18:07
1
import random
import string

def get_random_string(length):
    letters = string.ascii_letters + string.digits + string.punctuation
    result_str = ''.join(random.choice(letters) for i in range(length))
    print("Random string of length", length, "is:", result_str)

get_random_string(8)
get_random_string(8)
get_random_string(6)
get_random_string(11)
get_random_string(22)
  • Could this be what you are looking for? – FeddrikScheper Dec 21 '20 at 06:46
  • Your answer should use `secrets` rather than `random`; the question post is correct in this respect. See: https://stackoverflow.com/questions/61399901/random-password-generation-with-digit-letter-symbol/61458972#61458972 – Peter O. Dec 21 '20 at 15:22
1

For anyone looking for something a lot simpler, you could just use the faker package. There's a method .password() you can feed args to, to make your passwords more or less complex.

Obviously, install faker: pip install faker

Then, just 3 lines of code:

from faker import Faker
fake = Faker()
print(fake.password())

This will output a string of length 10, these are the default arguments you can use:

password(length=10, special_chars=True, digits=True, upper_case=True, lower_case=True)

This is just a simple run in my terminal:

from faker import Faker
fake = Faker()
fake.password()
'S9tBRdbf^C'
fake.password()
'NWN8FB4ku&'
fake.password()
'*0B7Z_TisD'
fake.password()
'L2R_bTkw_^'

Feel free to comment if you got questions.

Lafftar
  • 145
  • 3
  • 11