Generating and Checking Passwords in Python

During my typical day I find myself logging in to about a dozen or more services. Good password habits tell us to keep the passwords different for each service or username. Not only that, most websites require you to use special characters, upper and lower case, keep changing the password and my top 'favorite', replace your password every X amount of time. As computers are getting faster, cracking passwords is becoming easier. To compensate, we are told to use longer passwords, great. So in theory, we all have 32-64 character passwords that are supper strong and our privacy is kept safe. In reality, we have maybe a handful of passwords and the forgot password link (in hope that we used an email we actually have). So in a time where we have all our information online and kept by passwords we hope not to be cracked, so what can we do with this 'too many passwords' problem?

The answer to this question is short and simple, we do what we can. In this post we will look at how to randomly generate passwords and how to check their security level to some degree. So let us get to it. Generating passwords is something that is rather straight forward. To sum it all up, randomly choose X characters from a given set. We will look at a few ways to generate passwords starting from a simple one. All of them follow that same basic rule.


import random
import sys

def main(argv):

	if (len(sys.argv) != 2):
		sys.exit('Usage: simple_pass.py <password_length>')
    
	password = ''
	for i in range(int(argv[0])):
		password += chr(random.randint(33,126))
	
	print 'You new password is: ' + password

if __name__ == "__main__":
	main(sys.argv[1:])

Using the python random module and passing in via command line the length of the password, we are able to randomly generate a unique password. The passwords are built from all printable ASCII characters randomly chosen. Later we will come back to this example to test a checking password strength program. Here are some example runs of this program.

wpid-pythongenrandompassword1-2013-01-2-02-24.png

This is a nice and simple script anyone can write and by all means use and distribute. It might be a quick hack, but still is not enough to get to be a true generator. Before we move on to another, more sophisticated password generator, lets actually look at a really unique generator using python's lambda.


import random
import string

pass_gen = lambda length, ascii =  string.ascii_letters + string.digits + string.punctuation: "".join([list(set(ascii))[random.randint(0,len(list(set(ascii)))-1)] for i in range(length)])

To use this lambda simply call it with the length required, like so:

wpid-pythongeneratepasswordex2.2.1-2013-01-2-02-24.png

Most password requirements today specify the number of special characters, uppercase, lowercase and numbers require in a password. Using the python string library, we can use string attributes to choose a number of specific characters from each set. Now we can modify our password generator to accept the number of uppercase, lowercase, digits and special charcters. The new code and example output will look like this:


import random
import sys
import string

def main(argv):

	if (len(sys.argv) != 5):
		sys.exit('Usage: simple_pass.py <upper_case> <lower_case> <digit> <special_characters>')
    
	password = ''
	
	for i in range(len(argv)):
		for j in range(int(argv[i])):
			if i == 0:
				password += string.uppercase[random.randint(0,len(string.uppercase)-1)]
			elif i == 1:
				password += string.lowercase[random.randint(0,len(string.lowercase)-1)]
			elif i == 2:
				password += string.digits[random.randint(0,len(string.digits)-1)]
			elif i == 3:
				password += string.punctuation[random.randint(0,len(string.punctuation)-1)]
	
	print 'You new password is: ' + ''.join(random.sample(password,len(password)))

if __name__ == "__main__":
	main(sys.argv[1:])


wpid-pythongeneratepasswordex3-2013-01-2-02-24.png

So at this point we can generate randomize and customize passwords. Let us now move to checking a password strength algorithm and check what kind of passwords we are generating. We will start by using a modified version of a password checker found on Password Advisor. I had to modify the code to get it to run and to create some output. Take a look.


import re

def CheckPassword(password):
    strength = ['Blank','Very Weak','Weak','Medium','Strong','Very Strong']
    score = 1

    if len(password) < 1:
        return strength[0]
    if len(password) < 4:
        return strength[1]

    if len(password) >=8:
        score = score + 1
    if len(password) >=10:
        score = score + 1
    
    if re.search('\d+',password):
        score = score + 1
    if re.search('[a-z]',password) and re.search('[A-Z]',password):
        score = score + 1
    if re.search('.,[,!,@,#,$,%,^,&,*,(,),_,~,-,]',password):
        score = score + 1

    return strength[score]
    
def main():
	
		
	user_input = raw_input("Check: ")

	while(user_input != 'quit'):
		print CheckPassword(user_input)
		user_input = raw_input("Check: ")

if __name__ == "__main__":
	main()


wpid-pythonpasswordstrengthchecker2-2013-01-2-02-24.png

The program uses length and python's regular expressions to determine a score for the password. This is a fairly straight forward calculation. A password longer than 8 characters is awarded 1 point, longer than 10 is awarded an additional point. Then, for each character from a character set the password is awarded another point. The points corresponding to an index on the strength list and an appropriate rating is determined. Let us see how our three password generators from before hold up to this checker standards. (The code has been slightly modified for testing).


import passwordadvisor
import random
import string
import re
import sys 

def ex1(num):

	password = ''
	for i in range(int(num)):
		password += chr(random.randint(33,126))
	
	return password
	
ex2 = lambda length, ascii =  string.ascii_letters + string.digits + string.punctuation: "".join([list(set(ascii))[random.randint(0,len(list(set(ascii)))-1)] for i in range(length)])

def ex3(argv):
    
	password = ''
	
	for i in range(len(argv)):
		for j in range(int(argv[i])):
			if i == 0:
				password += string.uppercase[random.randint(0,len(string.uppercase)-1)]
			elif i == 1:
				password += string.lowercase[random.randint(0,len(string.lowercase)-1)]
			elif i == 2:
				password += string.digits[random.randint(0,len(string.digits)-1)]
			elif i == 3:
				password += string.punctuation[random.randint(0,len(string.punctuation)-1)]
	
	return ''.join(random.sample(password,len(password)))
	
def checker(argv):

	example_1 = ex1(argv[1])
	print example_1 + ' ' + passwordadvisor.CheckPassword(example_1)
	
	example_2 = ex2(int(argv[2]))
	print example_2 + ' ' + passwordadvisor.CheckPassword(example_2)
	
	example_3 = ex3([argv[3],argv[4],argv[5],argv[6]])
	print example_3 + ' ' + passwordadvisor.CheckPassword(example_3)
		
def main(argv):
	if (len(sys.argv) != 7):
		sys.exit('Usage: ex4.py <length1> <length2> <upper_case> <lower_case> <digit> <special_characters>')
		
	checker(sys.argv)

if __name__ == "__main__":
	main(sys.argv[1:])

python exp 3

Test runs have shown that any password of length 8 is considered medium to strong by this measures. Notably the third generator has generated the strongest password at all test runs when 2 characters were chosen from each set. I have tested out the passwords generated using the website appropriately named HowSecureIsMyPassword. I do not advise nor condemn you trying to type your real password, but I have checked our generated passwords using this site. It seems that by their standards any 8 characters long password will take anywhere for a day to 20 days for a desktop PC to crack. For a 16 characters long password it seems that it will take 412 trillion years. So we are still safe on that side. It was shocking to know that some other sites that rank passwords, like ThePasswordMeter ranked any 8 character password we generated as high while HowSecureIsMyPassword claims it will take a day to crack the same password. However, 16 character long passwords were considered strong at both sites.

wpid-howsecure1.1-2013-01-2-02-24.png

wpid-thepassmeter-2013-01-2-02-24.png

wpid-howsecure1.2-2013-01-2-02-24.png

wpid-thepassmeter2-2013-01-2-02-24.png

Let us look at least at one more measure of password strength called bit strength, used to measure the strength of random generated passwords. it follows a formula where each character contributes an amount of bits calculated by the following formula:

 log_{2}(N)

The results is multiplied by the number of characters and a final bit strength is calculated. The threshold recommend, according to Wikipedia, for most secure systems is 80-bits. Let us look at how this will look in code and how our generated passwords measure up to this threshold. The code is very similar to the previous example, only replacing the checker function. (Download code for password generator and checker)


def bit_strength(password):
	return str(math.floor(len(password)*math.log(94,2)))


python exp 4

Since the character set we have include 94 possible characters in all generators, we have a constant log value (~6.55). This means that no matter what password we generate, even 88888888 will have the same ‘bit strength’ as ‘1.U*1!lh’. We can all recognize that the latter one will be much harder to crack than the first one. This is because the formula applies for randomly generated passwords. I will let the statistician among us calculate the odds of generating 88888888 out of 8 characters with 94 possible values for each character. I will "guesstimate" that it is very close to 0. We can draw a graph following this information to determine the length threshold required for a randomly generated password to pass the bit strength threshold.

wpid-lengthofpasswordvstrength2-2013-01-2-02-24.png

You will notice that any password above 15 characters is over 100 on the bit strength index. This means that it will comply with the secure password guideline as indicated on Wikipedia. It also means that if you can remember a randomly generated password, you will need less than 32 characters to secure your password. Keep in mind that this is limited to random generated password and not human created passwords.

So what can you do? In the mean time keep remembering that regardless of the source of password, the length is a directly proportional to the password strength according to any calculation. At this point I have no real solution to the ‘too many passwords’ problem, but I do have hope. I will continue with my work on the matter and post any changes I might have. Keep your passwords secure and safe.

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.
  • snkmad

    Very good reading!

    • CptDeadBones

      Thanks. I try to take my time and write in a coherent matter.

  • Poeta Del Laberinto

    Very interesting. Thank's! Very simple.

    Saludos, desde Argentina!

    • CptDeadBones

      Thanks!

  • Pingback: What to download along with python? [on hold]