i am creating a login function and a part of this includes checking a password entered by the user against the password stored in the database.
The problem is, when the password is created during the sign up step, it is stored as an encrypted string using fernet. Now I tried to see if encrypting the password entered by the user with the same key would produce the same encrypted string value as the one in the database, but fernet will create unique strings using the same key on the same plaintext.
What other ways can i check a password entered against its encrypted counterpart in the database?
import sqlite3
from cryptography.fernet import Fernet
key = Fernet.generate_key()
fernet = Fernet(key)
def loginFunc():
email = input("What is your email: ")
password = input("what is your password: ")
#a variable that can be used to save the data from users database
emailList = '''SELECT * FROM users WHERE `email` = ? and `password` = ?'''
#takes arguments into variable
password_checkEnc = fernet.encrypt(password.encode())
inputs = (email, password_checkEnc)
#executes line 62 to save the data that matches the arguments from 'inputs' and stores in emailList
cursor.execute(emailList, inputs)
user = cursor.fetchone()
#if statement to check if the database holds the data that is being searched for, allowing either the user to login or not
if user is not None:
print("login successful")
else:
print("incorrect information")
Why not just decrypt the password and check that way? My understanding of fernet key encryption is to do exactly what you're describing, creating a random encryption of the value each time. The purpose of the key is to decrypt it later. It would probably help to see your code where you initially encrypt the password but just looking at what you have here, it looks like you're generating a new key, not using the same one as before. You have to actually save the key you create initially with something like this:
# key generation
key = Fernet.generate_key()
fernet = Fernet(key)
# string the key in a file
with open('C:/locationofyourchoosing/newkey.key', 'wb') as filekey:
filekey.write(key)
# using the key to encrypt
f = Fernet(key)
f.encrypt(databasepassword)
Then you use this same key to decrypt the value later like this:
# opening the key
with open('C:/locationofyourchoosing/newkey.key', 'rb') as filekey:
key = filekey.read()
f = Fernet(key)
f.decrypt(databasepassword)
Then from there you can check the equality of the password. Forgive me if this isn't what you're looking for but I'm not sure you can produce the same encrypted string each time as I believe that would defeat the purpose. I hope this helps and again, if I'm missing the mark completely, please let me know as I'm just a touch confused as to why you're wanting to check equality this way.
Related
I have created a login/signup script in python using sqlite3 and kivy. Affter creating the database, the user can enter their username, email and password. The password is hashed before being inserted into the database. The signup function works, however I am having an error with validating the user's username and password with those in the database, where the code outputs 'login sucessful' after a wrong username/password.
def login_menu_press(self):
login = False
lusername = self.login_username.text
lpassword = self.login_password.text
print(f"Your username is {lusername}, and your password is {lpassword}.")
self.login_username.text = ""
self.login_password.text = ""
# Hashing inputted password
context = CryptContext(
schemes=["pbkdf2_sha256"],
default="pbkdf2_sha256",
# More rounds = more secure, but slower to hash
pbkdf2_sha256__default_rounds=50000
)
# Hash the inputted password
hash_pass = context.hash(lpassword)
# Connect to database
conn = sqlite3.connect('users.db')
# Create cursor
c = conn.cursor()
# Query database
command = f"SELECT * from users WHERE username='{lusername}' AND Password = '{hash_pass}';"
c.execute(command)
if not c.fetchone():
print("Login successful")
login = True
else:
print("Login failed")
I belive that the error persits after the Query database comment, any help is appreciated, thanks!
I excpeted that login sucessful outputs when the username and password is supposed to be correct, which is correct. However, the code outputs this again when the username and password is anything else random.
Edit the password inputted by the user is hashed with the same algorithm that was used to hash the password that the user signs up with, therefore the hashed values will be the same.
Hash alghoritm which you used use salt, thus for exatly same input string it will output different hash values:
# Hash the inputted password
lpassword = "Rambo"
hash_pass = context.hash(lpassword)
print(hash_pass)
hash_pass = context.hash(lpassword)
print(hash_pass)
hash_pass = context.hash(lpassword)
print(hash_pass)
$pbkdf2-sha256$50000$wniP0br3XguBsJbSuvc.xw$vmebKLd0Uvz4IF7DbX/TzCgD5p1/cmPYApaf2eKeZ4w
$pbkdf2-sha256$50000$wJjT2tv7H8NY633vXYvROg$D0CxPTfO8jIaLPHvUtawhzKMX75LqDYSNa.z3Bf/58s
$pbkdf2-sha256$50000$tZbSWmvNmdPau9eas1ZKaQ$qMVGQWPtlLvFZ/WlG.iDO7.jIHhFiirMZw0gysfqXhs
So there is no way to match previously inserted and hashed password value. You have to use hash function which will always generate same value for same input string, like SHA-256 for instance. Replace:
hash_pass = context.hash(lpassword)
with:
import hashlib # on top of main.py
hash_pass = hashlib.sha256(lpassword.encode()).hexdigest()
And comment out this part of code:
context = CryptContext(
schemes=["pbkdf2_sha256"],
default="pbkdf2_sha256",
# More rounds = more secure, but slower to hash
pbkdf2_sha256__default_rounds=50000
)
Also you you have to change your condition: if not c.fetchone(): to if c.fetchone():
I created a database using django and created a html form to store the data into the database.
Now, I want to encrypt using pycryptodome and store the ciphertext into the database.
And I want the decrypted plaintext when I display the data from the database.
I have tried some basic encryption examples and algorithms from pycrytodome documentation.
Now, I want to encrypt and store the ciphertext into the database.
#This is the function where I want to encrypt the data in the get_password variable and store it
def add(request):
get_name = request.POST["add_name"]
get_password = request.POST["add_password"]
print(type(get_name),type(get_password))
s = Student(student_name=get_name,student_password=get_password)
context = { "astudent":s }
s.save()
return render(request,"sms_2/add.html",context)
#This is the example I have tried from pycryptodome documentation.
from Crypto.Cipher import AES
key=b"Sixteen byte key"
cipher=AES.new(key,AES.MODE_EAX)
data=b"This is a secret##!!"
nonce = cipher.nonce
ciphertext, tag = cipher.encrypt_and_digest(data)
print(nonce)
print(key)
print(cipher)
print(ciphertext)
print(tag)
cipher1 = AES.new(key, AES.MODE_EAX, nonce=nonce)
plaintext = cipher1.decrypt(ciphertext)
try:
cipher1.verify(tag)
print("The message is authentic:", plaintext)
except ValueError:
print("Key incorrect or message corrupted")
I want to encrypt the plaintext I enter into the database using the html form and I want to ciphertext to be stored into the database.
I want help with that.
i am relatively new to pyhton and the cryptography module, so i'm trying to learn the basics of encrypting and decrypting.
It all works fine when i encrypt a file and decrypt it on the same program, but if i try to just run a decrypt code on a pre-encrypted file (i used the same key, of course) i get an InvalidSignature error, followed by an InvalidToken.
Now, i assumed that for some reason the key didn't match, but they are indeed the same.
Then i thought that for some reason i was passing a string instead of a byte to the functions, or that there are some sort of conversion errors that might alter the encrypted message. But the encrypt-decrypt code works, so i can't figure why the decrypt-only should face errors.
At last, i had a look at the source code for the decrypt function, and tried to figure out if the time stamp had something to do with the error i get, but i couldn't get anything relevant since i'm not too experienced.
This is the encrypt-decrypt code: given a password by the user it encrypts and prints a file, that can decrypt right away.
import base64
import os
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
print("Insert password: ")
password_str = input()
password = password_str.encode()
salt = os.urandom(16)
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt,
iterations=100000, backend=default_backend())
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)
message = "A really secret message. Not for prying eyes.".encode()
token = f.encrypt(message)
file = open("text_encrypted.txt", "wb")
file.write(token)
file.close()
file = open("text_encrypted.txt", "rb")
data = file.read()
file.close()
token = f.decrypt(data)
file = open("text_decrypted.txt", "wb")
file.write(token)
file.close()
Now, that works just fine and i get the two files that contain the encrypted and decrypted message.
If i delete the:
message = "A really secret message. Not for prying eyes.".encode()
token = f.encrypt(message)
file = open("text_encrypted.txt", "wb")
file.write(token)
file.close()
part, i should be left with just a decryption code, that should work on a previously generated encrypted file, that should be decrypted by the same password.
I'm obviously missing something maybe trivial, since it raises both invalid signature and invalid token.
Thanks for your help
The encryption key you’re using is a result of a PBKDF2. In order for PBKDF2 to return the same encryption key it must get the exact same parameters. That includes salt, which in your example is generated randomly every time.
You need to store the generated salt together with the encrypted file in order to be able to decrypt it later.
I am trying to retrieve a value from MongoDB and set a string to it in Python with MongoDB. Here is my user registration function:
def registerAccount():
registerUsername = input('Please input a username to be registered into our database.')
registerPassword = input('Please input a password to be entered into our database.')
passwordHash = sha256_crypt.hash(registerPassword)
regDetails = {
"username": registerUsername,
"passwordhash": passwordHash
}
db = client.pinnacle
users = db.users
users.insert(regDetails)
and here is my login function:
def login():
db = client.pinnacle
users = db.users
pwHash = users.find_one({"passwordhash"})
print(str(pwHash))
loginUsername = input('Please input username to be signed in.')
loginPassword = input('Please input password to be signed in.')
# pprint.pprint(users.find_one({"username": "1"}))
# example = users.find_one({"username": "1"})
pbkdf2_sha256.verify(loginPassword, pwHash)
Basically, I need to search the database for the username, and get the string right after the username which is passwordHash. Then I will set pwHash to that string which will be checked with PassLib. Help is appreciated. Thanks!
Also if anyone is interested you can see my full code here.
this happens whenever i select to login, even after not typing in username or password
The error is caused by this line below:
pwHash = users.find_one({"passwordhash"})
Where you should specify a filter of key/value pair, instead it's only specifying a key. You should remove the line above (and the print line right after it). See also pymongo find_one() method for more info.
Also, in the same function login(), you have a typo for key username.
user = users.find_one({"userName": loginUsername})
Note that it should be lowercase username to be consistent with how you're storing them elsewhere in the code.
Lastly, you should replace
pbkdf2_sha256.verify(loginPassword, user['passwordhash'])
with below method, as this is how you encrypt it in the registerAccount()
sha256_crypt.verify(loginPassword, user['passwordhash'])
Please consider adding error checks throughout your code. i.e. if users.find_one() returns None do XYZ.
I am using the snippet below to encrypt user password before saving in the database.
from pbkdf2 import crypt
pwhash = crypt(password_from_user)
Example: $p5k2$$Y0qfZ64u$A/pYO.3Mt9HstUtEEhWH/RXBg16EXDMr
Then, I save this in database. Well locally, I can perform a check doing something like this:
from pbkdf2 import crypt
pwhash = crypt("secret")
alleged_pw = raw_input("Enter password: ")
if pwhash == crypt(alleged_pw, pwhash):
print "Password good"
else:
print "Invalid password"
but how do I perform checks with what is on the db as the encrypted string is not always the same. I'm using python-pbkdf2.
Okey, Did more research and figured out that to achieve this, i first have to encrypt the password and save in db.as:
pwhash = crypt("secret",iterations=1000)
which can produce a string like $p5k2$3e8$her4h.6b$.p.OE5Gy4Nfgue4D5OKiEVWdvbxBovxm
and to validate when a user wants to login with same password, i use the function below:
def isValidPassword(userPassword,hashKeyInDB):
result = crypt(userPassword,hashKeyInDB,iterations = 1000)
return reesult == hashKeyInDB #hashKeyInDB in this case is $p5k2$3e8$her4h.6b$.p.OE5Gy4Nfgue4D5OKiEVWdvbxBovxm
this method returns True if the password is same or False if otherwise.