so before i start AS-level computer science next year, i have a little practice task to do over the summer.
Its a game that requires a log in with username and password, and then requires to store the users scores.
Originally i was just going to have a .txt file named after each user, with the first line being a plaintext password, which the program reads during login. to save the score i was going to append the raw score onto a new line at the end of the document.
Now i think about it, im wondering if it would be better and tidier to have a .csv file and save all the usernames of each user in the first column, then a salted and hashed password on the second column, relating to the user by being in the same row (mainly because i think thats interesting and would like to learn it, rather that it being necessary for my program) then having the next rows each score taken.
I don't really know how i would go about checking the first column to find if there is a username the same as the user is trying to input, then how i would compare the password inputted to the password in same row but one column along
Any advice will be greatly appreciated, however in the meantime i will try and figure it out (afterall you should "Code to Learn, not Learn to Code")
i will post back if i get anywhere
thanks in advance for any help :)
Alex
I don't think csv is the right format to store passwords in Python. How about using json?
I've written some example which wraps around some dict, and stores it's content into a json-file:
import codecs
import crypt
import hmac
import json
import os
class Secret:
def __init__(self, filename='passwords.json'):
self.filename = filename
self.content = self.read_file()
def read_file(self):
if os.path.exists(self.filename):
with codecs.open(self.filename, 'r', encoding='utf-8') as rf:
return json.loads(rf.read())
# create defaults if file does not exist
return dict(
salt=crypt.mksalt(),
users=dict()
)
def write_file(self):
with codecs.open(self.filename, 'w', encoding='utf-8') as wf:
return wf.write(json.dumps(self.content, sort_keys=True, indent=4))
def set_user_password(self, name, password):
self.content['users'][name] = crypt.crypt(name, password)
self.write_file()
def check_user_password(self, name, password):
if name in self.content['users']:
hashed = self.content['users'][name]
if hmac.compare_digest(hashed, crypt.crypt(name, password)):
return True
return False
Please note:
This Example is blindly based on the documentation of the crypt-module - I have no clue if this in any way secure (probably not).
Further: I don't know if it's good to store the salt alongside the passwords.
You can use it like this:
if __name__ == '__main__':
secret = Secret(filename='passwords.json')
secret.set_user_password('demo_user', 'lolcat')
for user, password in [
('demo_user', 'lolcat'),
('demo_user', 'wrong_pass'),
('wrong_user', 'wrong_pass'),
]:
print(
'user:', user, 'password:', password,
'\n-->', secret.check_user_password(user, password)
)
It creates a json-File like this:
{
"salt": "rT",
"users": {
"demo_user": "lo1JY.PCooh4."
}
}
Related
In summary I'm trying to create a password manager. The Idea is that the program would ask the user input.
If user writes "new", the program asks input on the website, username and password and then store this data in a text file in the form of a List.
Now the main problem:
I want to be able to access selected data and have the program print said data from the text file to me.
For example:
I input into the program the website "google" along with username: "potato" and password: "potato"
After that, the program asks me what else I want to do. And if I write "access google", I want to program to give me back the website, username and password that are SPECIFIC to the google input.
This is necessary, as I will be adding several different inputs.
I have no idea how to do this and have no tutor. I hope someone can give a solution I can learn from.
Below you will find the base code I have come up with.
Keep in mind that I am a beginner. Thank you.
vault = open("Passvault.txt", "r+")
list = []
action = input("What do you want to do? ")
def tit():
global title
title = input("Add website: ")
return title
def user():
global username
username = input("Create username: ")
return username
def passw():
global password
password = input("Create password: ")
return password
running = True
while running:
creation = True
tit()
user()
passw()
if action == "new":
tit()
user()
passw()
#I added a class here hoping that i could create a class with an argument referencing the title
#so that when i type access "title" in the next if statement it would print back the data
#relevant to the selected title
class new(str(title)):
list.append(tit)
list.append(user)
list.append(passw)
vault.close()
if action == "access" + title:
creation = False
print(title)
print("Username: " + username)
print("Password: " + password)
vault.close()
Here is the code. This code stores the username, password, and website name in the txt file and also prints the username and password w.r.t website name.
import re # used to search patterns.
file_name = 'Passvault.txt'
while True:
action = input("What do you want to do? ")
if action == 'new':
title = input("Add website: ")
username = input("Create username:")
password = input('Create password')
#writing data into the file
with open (file_name,'a') as f:
data = f.write(f'{title} {username} {password}\n')
if 'access' in action:#if input contains access word
website = action.split()[1].strip() #storing website name which is written after access
#reading the data
with open(file_name,'r') as f:
data = f.read()
#searching for all username password related to website
username_pass = re.findall(f'{website}\s+(.*?)\n',data,re.S)#example google sachin 1234
print('Website: ',website)
print('Username, Password',username_pass)
I'm going to give you a more conceptual answer than code, because this is a more long term type of deal.
What you're going to want to do is use json files and dictionaries to store your data so you can search by keys (see the dictionaries link).
Once you've done that you're going to want to wrap everything in a while loop inside a def that gets user input like so:
def get_input():
permitted_actions = ['new', 'access', 'exit']
while True:
action = input("What do you want to do? Valid actions: new, access or type EXIT to end the program.").strip().lower()
if action not in permitted_actions:
print(f"{action} is not a valid action!")
elif action == 'new':
#call another function to do stuff here
elif action == 'access':
#call another function to do stuff here
elif action == 'exit':
print("Shutting down...")
break
I would highly recommend against creating your own actual vault for a password manager until you're much more experienced if you actually intend to use this, otherwise feed it fake passwords and whatnot and learn.
Now when you're adding website data you'll read your dictionary (see the dictionary link) and get the key associated with said website if it exists and then update the info that the user gives.
When you're accessing a website's data you just go to the dictionary (see the dictionary link) and grab the info relating to that website key if it exists.
Remember, you're going to be loading that dictionary from a json file (see the json link).
If you were to make this program an actual program someone would use you'd use a proper database of some sort (python3 has sqlite support natively) and use a database with encryption and master passwords.
I hope this points you in the right direction.
You can save the values as a dictionary with a list as the username and password then use literal_eval to convert the string dict into a dict and access the username and password as well as storing other websites with its own usernames and passwords.
website = 'google'
username,password = 'potato', 'potato'
filename = "yourfilehere"
with open(filename, w) as f:
f.write(str({website: [username,password]}))
#This will save your data as a string dictionary will the data above
#then read the file, get the dictionary and convert it then use its values
from ast import literal_eval
with open(filename, 'r') as f:
data = f.read()
data = literal_eval(data)
#Then search dictionary for website
found = data.get(website)
#Then if it was successful get the username and password
username = found[0]
password = found[1]
As long as you only have the dictionary created as a string in the file you are reading you can use this ethod to save as many website with the allocated username and password saved with it.
You can add a input() into the code to check which site the user wants the username and password for and then search for it in your dictionary.
search = input("Enter the website: ")
try:
found = data.get(search)
#add code here to get username and password
except:
print("failed to find a website matching: %s" % search")
I have a user table, and it has a password for payment. I decided to encrypt all this information. Each API retrieves or inserts new data, it always calls a function to encrypt the password like this.
from cryption import encrypt, decrypt
enc_password = encrypt(password)
data = User(id=id, password=enc_password)
db.add(data)
db.commit()
Since I encrypt this in API, it looks redundant. also, sometimes I forgot to do add encrypt code and it causes critical errors. So, I wonder If I can do this on models instead of doing this. If I can do encrypt before insert data and decrypt when return data on query level, it will be nice.
First of all, an important warning:
WARNING: NEVER store passwords of users. NEVER. The moment your server is compromised the hackers will not only have your server, they will have the encryption key and so have all the passwords.
Instead, store password hashes. See Why is password hashing considered so important? and Difference between Hashing a Password and Encrypting it.
In Python, use passlib to handle password hashing for you.
With that out of the way, you can automatically store password hashes in your database, or make any other data transformation, by using properties to make the transformation. I use this in my user models.
In the following sample model, the password property actually sets the password_hash column, to a hashed value:
from passlib.context import CryptContext
PASSLIB_CONTEXT = CryptContext(
# in a new application with no previous schemes, start with pbkdf2 SHA512
schemes=["pbkdf2_sha512"],
deprecated="auto",
)
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
# site-specific fields, like name, email, etc.
# password hash handling
# pbkdf2-sha512 is 130 characters short, but it never hurts to
# leave space for future growth of hash lengths.
password_hash = db.Column(db.String(256), nullable=False)
def __init__(self, password=None, password_hash=None, **kwargs):
if password_hash is None and password is not None:
password_hash = self.generate_hash(password)
super().__init__(password_hash=password_hash, **kwargs)
#property
def password(self):
raise AttributeError("User.password is write-only")
#password.setter
def password(self, password):
self.password_hash = self.generate_hash(password)
def verify_password(self, password):
return PASSLIB_CONTEXT.verify(password, self.password_hash)
#staticmethod
def generate_hash(password):
"""Generate a secure password hash from a new password"""
return PASSLIB_CONTEXT.hash(password.encode("utf8"))
This lets me use User(..., password=password) and it'll automatically hash the password for me. Or update the password with if new_password == new_password_again and some_user.verify_password(old_password): some_user.password = new_password without having to remember how to hash the password again.
You can also use 'hidden' columns to store any other data you need to encrypt on storing, decrypt on retrieving. Name your model attributes with a leading _ underscore to mark them API private, then pass in the real column name to the Column() object as the first argument. Then use a property object to handle encryption and decryption:
class Foo(db.Model):
__tablename__ = "foo"
id = db.Column(db.Integer, primary_key=True)
# encrypted column, named "bar" in the "foo" table
_bar = db.Column("bar", db.String(256), nullable=False)
#property
def bar(self):
"""The bar value, decrypted automatically"""
return decrypt(self._bar)
#bar.setter
def bar(self, value):
"""Set the bar value, encrypting it before storing"""
self._bar = encrypt(bar)
This is covered in the SQLAlchemy manual under Using Descriptors and Hybrids. Note that there is no point in using a hybrid_property in this case, as your database can't encrypt and decrypt on the server side.
If you do encrypt, keep your keys separate from your source code, and make sure you rotate your keys (keep old keys at hand to decrypt old data not yet re-encrypted), so that if a key ever is compromised you can at least replace it.
Pick a good, trusted encryption recipe. The cryptography package includes the Fernet recipe, see another answer of mine for advice on how to use it, then upgrade to the MultiFernet() class to manage key rotation.
Also, read up on key management best practices. Cryptography is very easy to get wrong.
I have written some code for a system, I have imported another file which stores all the information for login details. But when I go to test it and try to login it keeps coming up with "INCORRECT". Both of the code files are attached.
I have tried changing the names of the files, variables and changing the login details but it still doesn't work.
from database import user_data, pasw_data, no_file
name = user_data
code = pasw_data
def user_check():
user = input("USERNAME >>")
if user == name:
pasw_check()
else:
print("INCORRECT")
def pasw_check():
pasw = input("PASSWORD >>")
if pasw == code:
print("ACCESS GRANTED")
user_check()
This is the file, which stores all the login info, named database.py
user_data = ["123"]
pasw_data = ["python"]
You're checking a string (user) and a list (user_data) for equality. They aren't equal at all. The list just happens to contain a string that's equal to your query. You should use in to search lists (and strings, dictionaries, tuples, etc) for data:
if user in user_data:
print("I'm in!")
I am quite new to the Python world and thought it would be great to work through my son's Python book for beginners. I am in the process of combining all the current chapters into creating an interactive 'Choose from list...' in order to familiarise myself with the basics. One of these sections included validating a username/password stored in the code itself, but I thought I would take it a step further and see if I can read from a file.
I have managed to cobble together the following but it will only ever read from the latest line in the password file:
with open('password.csv', "r") as csvfile:
reader = csv.DictReader(csvfile)
database = [] #creates a list holder for the data
for row in reader:
database.append(dict(username=row["username"],
password=row["password"]))
authenticated = False
while not authenticated:
username = input("Enter the username: ")
password = input("Enter the password: ")
for row in database:
userFile = row["username"]
passwordFile = row["password"]
if (userFile) == username and (passwordFile) == password:
authenticated = True
if authenticated is not True:
print("Incorrect credentials")
Can anybody with a little more experience point me where I have gone wrong? Is this too advanced for my level and should I have done something different?
Thanks for your suggestions but I managed to sort this myself and was simply down to my inexperience in Python. The spacing was incorrect in my code. Simple!
I'm currently getting my head around the hash feature in Python to encode with an algorithm a PIN number. After I have the user set their PIN number, I set it to the variable 'actualPIN'. My code is as follows below:
def returnCard(name, ID, rollingBalance, actualPIN):
PIN = hashlib.sha256()
PIN.update(b"actualPIN")
data = (rollingBalance, actualPIN, ID)
print(rollingBalance)
with sqlite3.connect("ATM.db") as db:
cursor = db.cursor()
sql = 'update Atm set Balance=?, PIN=? where CustomerID=?'
cursor.execute(sql, data)
db.commit()
print("Thank you for using Norther Frock")
print("Returning card...")
time.sleep(1)
print("Have a nice day")
entryID()
Everything works, however the pin which the user enters is saved on the database. What I want to save to the database is the encoded password (obviously?). Could anyone explain how I could do this?
You are writing the actualPIN variable to the database. Instead, you meant to write the digest:
data = (rollingBalance, PIN.digest(), ID)
# or data = (rollingBalance, PIN.hexdigest(), ID)
And you probably want to use actualPIN variable, not "actualPIN" string here:
PIN.update(repr(actualPIN).encode('utf-8'))