Before we dive in to the topic, I would like to apologies. Over the past 10 days, I have had multiple matters that require my attention. While I try to allocate some time for everything, this blog has not been on the top of my priorities. This was a mistake, I now realize. I should have anticipated some of the issues and plan ahead accordingly. The opposite is what happened. I cannot say this will not happen again, I can only apologies and try to do better in the future. So let us get started.
The last post I wrote talked about how to password protect your python program. Although this is very neat and sufficient in most cases, what you really need is a method to authenticate users and password. This post will be rather simple and straight to the point. Much of the work it relies on we have done already. Recall at this point we are storing the password as encrypted strings in a file, independent of our python program. Now we just have 2 things we have to do. We will start by recognizing a single user and allow him/her accesses. Then, we will expand the program to allow multiple allowed users accesses. As noted before, we are going to use Python2.7, I strongly encourage referring to the documentation or download the documentation for python2.7 for offline reference.
As we did previously, before we can authenticate any user we have figure out how we want to store the data we are comparing to. You may recall we used a support program to create a file and store the password. Now we will extend the “helper” script to store a user name on a single line, followed by the encrypted password in the next line. This format may be modified at your discretion. For example, you may choose to encrypt both the user name and the password. You may also choose to have all the information as a single line, separated by a vertical line, ‘|’, or a comma, ‘,’. The options are multiple, but the principle is the same. Assuming we follow the first suggested format, here is how the code to store a user name and password will look like:
import sys import hashlib import getpass def main(argv): if len(argv) != 1: sys.exit('Usage: store_user_pass.py <file_name>') print '\nUser & Password Storage Program v.01\n' if raw_input('The file ' + sys.argv + ' will be erased or overwrite if exsting.\nDo you wish to continue (Y/n): ') not in ('Y','y') : sys.exit('\nChanges were not recorded\n') user_name = raw_input('Please Enter a User Name: ') password = hashlib.sha224(getpass.getpass('Please Enter a Password: ')).hexdigest() try: file_conn = open(sys.argv,'w') file_conn.write(user_name + '\n') file_conn.write(password + '\n') file_conn.close() except: sys.exit('There was a problem writing to the file!') print '\nPassword safely stored in ' + sys.argv + '\n' if __name__ == "__main__": main(sys.argv[1:])
Note that the changes from our previous version, store a single encrypted password, have not changed much. The main thing we have done is added a user_name variable that gets written to the file. As before, we warn the user from alteration of a file might already exist. Here is an example output of the program:
Here are the content of the file pass_db.txt:
Now we are ready to see how the other side of this program might look like. Since we know how to verify the password, adding the user name option is not much change. For our application purpose, we will terminate the program if the user name is not known or x password attempts have been made. Here is how the code for this will look like:
import sys import hashlib import getpass def main(argv): if len(argv) != 1: sys.exit('Usage: user_pass.py <file_name>') print '\nUser & Password Authentication Program v.01\n' try: file_conn = open(sys.argv) user_name = file_conn.readline()[:-1] password = file_conn.readline()[:-1] file_conn.close() except: sys.exit('There was a problem reading the file!') pass_try = 0 x = 3 if raw_input('Please Enter User Name: ') != user_name: sys.exit('Incorrect User Name, terminating... \n') while pass_try < x: user_input = hashlib.sha224(getpass.getpass('Please Enter Password: ')).hexdigest() if user_input != password: pass_try += 1 print 'Incorrect Password, ' + str(x-pass_try) + ' more attemts left\n' else: pass_try = x+1 if pass_try == x and user_input != password: sys.exit('Incorrect Password, terminating... \n') print 'User is logged in!\n' if __name__ == "__main__": main(sys.argv[1:])
Running the code with the file we generated before, pass_db.txt:
Like previous programs, the user is prompt for a password 3 times. At each trial the user is notified the number of password attempts reaming. At this point, if you have been following along, you should be able to pice together how this might be extended to multiple users. For our final version we will have 2 python files, one to generate a password file and the other to log the user in. The modification for the storage code needed at this point are minimal, all we need to change is the mode we write to the file. In previous versions we used ‘w’ for write mode. Now we are going to use ‘a’, which is only going to append the new information to the end of the file. If the file is not yet created, the file will be created.You can look users and password storage program.
Last but not least, here is our final version of the authentication module. First the code:
import sys import hashlib import getpass def process_file(file_name): user_names =  passwords =  try: file_conn = open(file_name) data = file_conn.readlines() for i in range(len(data)): if i%2 == 0: user_names.append(data[i][:-1]) else: passwords.append(data[i][:-1]) file_conn.close() except: sys.exit('There was a problem reading the file!') return user_names, passwords def main(argv): if len(argv) != 1: sys.exit('Usage: user_pass.py <file_name>') print '\nUser & Password Authentication Program v.01\n' user_names, passwords = process_file(sys.argv) pass_try = 0 x = 3 user = raw_input('Please Enter User Name: ') if user not in user_names: sys.exit('Unkown User Name, terminating... \n') while pass_try < x: user_input = hashlib.sha224(getpass.getpass('Please Enter Password: ')).hexdigest() if user_input != passwords[user_names.index(user)]: pass_try += 1 print 'Incorrect Password, ' + str(x-pass_try) + ' more attemts left\n' else: pass_try = x+1 if pass_try == x: sys.exit('Incorrect Password, terminating... \n') print 'User is logged in!\n' if __name__ == "__main__": main(sys.argv[1:])
So what is different? Now instead of just reading one user name and password, we read more. The information is stored in 2 (2things) lists that have corresponding indexes. So the user name at index 1 has an encrypted password stored at index 1 of the password list. All other operation are the same as any previous example. I do not think there is a need to include an input, it is the same as before.
For a final remark, I realize that the method I discussed in this and the previous post and this one are not very secure or useful outside of an educational exercise. In ‘real world’ applications the information will be stored in a database. There are some software that has already been pre written for user authentication in python. The bottom line is that user authentication is not enough to protect an application. There are several more methods we may or may not cover in the future. For the near time, we will start to look into security algorithms and application.