Aryan Aryan - 1 month ago 20
Python Question

Python - Help needed with file reading

I am trying to create a simple login system. What I'm doing is storing the login data in a text file called 'accounts.txt'

Now, when user tires to login, it first checks if the username given by the user is in the 'accounts.txt'. If it exists, then it asks for the password and then checks if password matches with the password in 'accounts.txt'

fr = open('accounts.txt', 'r')
while True:
username = input('Enter your username: ') # Ask for their username

if username in fr.read(): # Check if username exists
password = input('Enter password: ') # Ask for password if username exists

if username+password in fr.read():
print('Welcome ' + username)
break
else:
print('Wrong password')


Note, the password save in accounts.txt is in the format of usernamepassword so if username is jack and password is gate, the actual password in the txt file will be jackgate, hence im using username+password to check if password is correct.

The problem occuring is if the user enters correct username, then program moves ahead properly but even if the password entered is right, it still displays 'Wrong password' .When the second time user enters username, it even shows error for wrong username. I tried to play with the code for a long time but couldn't come up with a solution. I guess it has something to do with fr.read(). Can I use that 'fr' object only once?

Answer Source

Let me suggest some improvements with my answer to your question. I would read the accounts file in its entirety so you have an in-memory structure. If you do this as a dictionary in the form accounts[USER] -> PASS you can easily check for any account as per the code below.

Regarding my suggestions (they do not exactly only answer your questions, but IMHO the topic of writing login code should be treated with care):

  • I strongly recommend not to store passwords in plain text, regardless of application importance, always use hashes.
  • Do not store just the password hash, always use salting.
  • Do not tell the person trying to log in, if the username or the password was wrong, always just say "that's not the right combination", thus making it harder to break in.
  • Please find information about hashing functions in Python here: https://docs.python.org/3/library/hashlib.html#randomized-hashing
  • This site has a good introduction on salting ans securing passwords: https://crackstation.net/hashing-security.htm
  • Do you users a favor and treat the username as no case-sensitive. That is a totally valid approach, but it annoys me every time I have to use such a site (just like email addr are not case-sensitive)
  • As I am a total layman regarding password security, maybe one of the other Stackoverflow users can jump in with a comment and expand on this topic.

Anyway, here is my answer for your question on how to check for a login. I created a function check_account() that returns True or False, depending on wether the supplied credentials were correct or not.

import hashlib
import os
import binascii

def check_account(usr, pwd):
    # read the accounts file, a simple CSV where
    # username, salt value and password hash are
    # stored in three columns separated by a pipe char
    accounts = {}
    fr = open('/users/armin/temp/test.csv', 'r')
    for line in [x.strip().split("|") for x in fr.readlines()]:
        accounts[line[0].lower()] = (line[1], line[2])
    fr.close()

    # now go looking if we do have the user account
    # in the dictionary
    if usr in accounts:
        credentials = accounts[usr]
        # credentials is a list with salt at index 0
        # and pwd hash at index 1
        # generate the hash form the functions parameters
        # and compare with our account
        h = hashlib.blake2b(salt=binascii.unhexlify(credentials[0]))
        h.update(pwd.encode('utf-8'))
        if credentials[1] == h.hexdigest():
            return True
        else:
            return False
    else:
        return False


def main():
    while True:
        username = input('Enter your username: ')  # Ask for their username
        password = input('Enter password: ')  # Ask for password if username exists
        if check_account(username.lower(), password):
            print("Welcome, {0}".format(username))
        else:
            print('Username or password unknown')


if __name__ == '__main__':
    main()

To create the data for a user account, use may this code.

def create():
    username = input('Enter your username: ').lower()  # Ask for their username
    password = input('Enter password: ')  # Ask for password if username exists
    salt = binascii.hexlify(os.urandom(hashlib.blake2b.SALT_SIZE))
    print("SALT value:", salt)
    h = hashlib.blake2b(salt=binascii.unhexlify(salt))
    h.update(password.encode('utf-8'))
    print("Pwd hash:", h.hexdigest())