So I've recently learned how to store passwords in a DB, that is by adding a salt to the plaintext password, hashing it, and then storing the hash.
I'm working on a really small Flask app to try all this out, but I'm having a problem with the password hashing and checking parts of the process. It seems that I"m ending up with two different hashes for the same input and I can't seem to figure out why.
I ran a little experiment in the interpreter to test things out.
>>> from os import urandom
>>> salt = urandom(32).encode('base-64')
>>> salt
'+3DejJpQZO9d8campsxOB6fam6lBE0mJ/+UvFf3oG8c=\n'
>>> plaintext_pw = 'archer'
>>> plaintext_pw
'archer'
>>> salted_pw = plaintext_pw + salt
>>> salted_pw
'archer+3DejJpQZO9d8campsxOB6fam6lBE0mJ/+UvFf3oG8c=\n'
>>> from flaskext.bcrypt import Bcrypt
>>> bc = Bcrypt(None)
>>> hashed_pw = bc.generate_password_hash(salted_pw)
>>> hashed_pw
'$2a$12$znMwqAw.GliVE8XFgMIiA.aEGU9iEZzZZWfxej5wSUFP0huyzdUfe'
All is working well at this point, but when I turn around and do this:
>>> bc.generate_password_hash(plaintext_pw + salt)
'$2a$12$qbywkEjuJgmBvXW6peHzAe.rWjoc.ybFKRNzuZhom2yJSXaMRcVTq'
I get a completely different hash, even though I started with the same plaintext_pw and salt. I thought that wasn't supposed to happen? Furthermore each subsequent call to bc.generate_password_hash() gives me different results each time:
>>> bc.generate_password_hash(plaintext_pw + salt)
'$2a$12$FAh9r4oaD40mWPtkClAnIOisP37eAT5m.i.EGV1zRAsPNbxg3BlX2'
>>> bc.generate_password_hash(plaintext_pw + salt)
'$2a$12$gluk9RUiR6D0e2p1J.hNgeE3iTFxDUlCNvFJOsCZZk89ngO.Z6/B6'
As far as I can tell plaintext_pw and salt aren't changing between calls. I can't seem to spot the error here, could someone explain to me exactly what's happening here, and what it is I'm doing wrong?
Ok so it looks like I've solved my problem. Turns out I wasn't using bcrypt properly. Here's what I learned:
The hashes were different each time I called generate_password_hash because bcrypt automatically generates a salt for you and appends it to the hashed password, so no need to generate it with urandom or store it separately.
I didn't talk about this in my post, but its worth noting here anyway - I assumed that on login you would need to call generate_password_hash() and provide the password from the login form to create a second hash for check_password_hash() to compare against, but that isn't necessary. check_password_hash() can be called with the stored hash and the form password (respectively) and it will automatically take care of salting and hashing the form password, and comparing it to the stored hash.
And with that everything is working fine now. Hope this helps someone else!
Related
Preamble:
I did search StackOverflow and I know someone had a question like mine, but now (a couple months after first seeing it), I could not find that answer again. I'm fully aware there is likely a duplicate, but for the life of me, I cannot find it in my searches.
The problem:
I have a cryptography module which generates a hashed password used for encrypting and decrypting secrets stored in a TinyDB database. All the functionality works except for decrypting the secret. The password is verifying correctly, so I know that isn't the issue. I am almost positive my issue is in getting the salt encoded properly in the decryption function.
Encryption code:
pas = use_password(args[0])
salt = urandom(16)
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,salt=salt,iterations=100000,)
sec = base64.b64encode(kdf.derive(bytes(pas,'utf-8')))
token = Fernet(sec).encrypt(bytes(key,'utf-8'))
salt = base64.b64encode(salt).decode('utf-8')
return token, salt
Decryption:
pas = use_password(pw)
***salt = base64.b64decode(salt)***
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,salt=salt,iterations=100000,)
sec = base64.b64encode(kdf.derive(bytes(pas,'utf-8')))
# BUG: Reported defects -#ctrenthem at 7/26/2021, 8:07:00 PM: cryptography.fernet.InvalidToken error
key = Fernet(sec).decrypt(bytes(token,'utf-8'))
return key # Returns a byte object which may need to be converted to a string.
What I tried:
I've already tried different variations of duplicating the salt encryption, but keep getting errors. Part of the problem is that base64.encode requires two "file objects" for input and output, and does not accept a string variable, which makes it unusable for my needs.
I could resolve this by creating a temp file, but that would be the worst solution because it involves
creating a new file in the filesystem with partially decrypted information, outside of RAM and the database, weakening the security of the whole system
Creates a new temp file, which is just a waste of system resources
Adds a whole lot more code just to implement something that I am positive can be implemented differently with one or two words.
Despite knowing that there is another solution, I cannot figure it out and am lost as to which base64 or bytes function will accomplish the job.
I'm hashing a password as follows:
mysalt = os.urandom(12).encode('hex')
passhash = mysalt + pw
hash = str(pbkdf2_sha256.encrypt(passhash))
How can I verify this password with user entry? The following:
hash3 = str(pbkdf2_sha256.encrypt(passhash))
...returns a completely different value.
Thanks in advance.
Make sure you use the same salt when you store the password as when you're checking it: passhash values with different salts will result in different hashes.
You could either use the same salt for every password (more convenient), use and store a different salt for every user (more secure), or a combination of both.
Edit: You didn't specify which library you were using to get pbkdf2_sha256. It could be that that library can handle salt-related tasks by itself. PassLib, for example, will generate a salt for you and include it in the result. It offers a verify method to check the user's input against that result.
Would advise against comparing the resulting strings - often encoding gets in the way. Try comparing the actual bytes.
the solution is:
import os
from passlib.hash import pbkdf2_sha256
mysalt=os.urandom(12).encode('hex')
hash = pbkdf2_sha256.encrypt("password")
print hash
print pbkdf2_sha256.verify("password", hash)
pwd='sefrhiloliutzrthgrfsdyv<sef234244567!"234wsdycvhn'
mypass =mysalt+pwd
print mypass
hash2 =pbkdf2_sha256.encrypt(mypass, rounds =200000)
hash1= pbkdf2_sha256.encrypt(pwd, rounds=80000, salt_size=100)
print"hash2: ", hash2
print pbkdf2_sha256.verify(mypass,hash2)
After reading here a bit about salting passwords, it seems that it's best to use a unique salt for each user. I'm working on implementing Flask-Security atm, and from the documentation it appears you can only set a global salt: ie SECURITY_PASSWORD_SALT = 'thesalt'
Question: How would one go about making a unique salt for each password?
Thanks!
edit: from the docs on Flask-Security, I found this, which seems to again suggest that this module only uses a single salt for all passwords out of the box.
flask_security.utils.get_hmac(password)
Returns a Base64 encoded HMAC+SHA512 of the password signed with the salt
specified by SECURITY_PASSWORD_SALT.
Yes, Flask-Security does use per-user salts by design if using bcrypt (and other schemes such as des_crypt, pbkdf2_sha256, pbkdf2_sha512, sha256_crypt, sha512_crypt).
The config for 'SECURITY_PASSWORD_SALT' is only used for HMAC encryption. If you are using bcrypt as the hashing algorithm Flask-Security uses passlib for hashing and it generates a random salt during hashing. This confustion is noted in issue 268: https://github.com/mattupstate/flask-security/issues/268
It can be verified in the code, walking from encrypt to passlib:
flask_security/utils.py (lines 143-151, 39, and 269)
def encrypt_password(password):
...
return _pwd_context.encrypt(signed)
_pwd_context = LocalProxy(lambda: _security.pwd_context)
flask_security/core.py (269, 244-251, and 18)
pwd_context=_get_pwd_context(app)
def _get_pwd_context(app):
...
return CryptContext(schemes=schemes, default=pw_hash, deprecated=deprecated)
from passlib.context import CryptContext
and finally from: https://pythonhosted.org/passlib/password_hash_api.html#passlib.ifc.PasswordHash.encrypt
note that each call to encrypt() generates a new salt,
Turns out that if you use bcrypt, it takes care of the salting and stores it with the hash. So I'll go that route!
Thanks to this topic which lead me to this discovery:
Do I need to store the salt with bcrypt?
I am a beginner so if this question sounds stupid, please bear with me.
I am wondering that when we write code for username/password check in python, if it is not compiled to exe ie script state, won't people will easily open the file and remove the code potion that is doing the password check?
I am assuming that the whole program is entirely written in python, no C or C++.
Even if I use a program like py2exe it can be easily decompiled back to source code. So, does that mean it is useless to do a password check?
How do professional programmers cope with that?
Edit: Your revised question makes clear that you're concerned about people editing the code to bypass a password check. Yes, that is quite possible. You can deliver your code in .pyc form, but that won't necessarily prevent someone from decompiling and altering it. Unfortunately, Python's just not designed to prevent code alteration. The best you can do is perform some kind of authentication transaction with a secure server, so that no matter how someone alters the code, they can't bypass that step. Depending on your exact application, that might be overkill.
The problem of how to manage password authentication is a tricky security problem on which people spend entire careers. However, here's some information about it, that assumes that you're trying to roll your own password authentication from scratch:
Even for casual password protection, as a general rule, user passwords are not stored in a plaintext form. Instead, usually a reliable one-way hash function is used to create a bit pattern that doesn't resemble the password. When a password is entered, the same hash function is applied and the bit patterns are compared. If they're the same, the likelihood is quite high that the password was entered correctly.
What constitutes a "reliable" hash function is tricky. Several are in common use, and some of the common hash functions are susceptible to known exploits.
Noelkd provides some code that demonstrates this approach, although MD5, which his code uses, is (I believe) one that's been compromised to an extent that there are better choices out there. This article also offers some code to do something similar:
Authentication of Users and Passwords in Python
If your concern is storing the actual password that you have to pass to the SQLite database in plaintext, that's a different problem. Most of the time, I've seen such passwords stored in plaintext in either scripts or a configuration file, and the application is structured in such a way that compromising that password is a matter of modest risk.
If you are doing the checking on a user's machine, they can edit the code how they like, pretty much no matter what you do. If you need security like this then the code should be run somewhere inaccessible, for instance a server. "Don't trust the client" is an important computer security principle.
I think what you want to do is make a server script that can only be accessed by a password being given to it by the client program. This server program will function very much like the example code given in other answers: when a new client is created they send a plaintext password to the server which puts it through a one-way encryption, and stores it. Then, when a client wants to use the code that is the main body of your program, they send a password. The server puts this through the one-way encryption, and sees if it matches any stored, hashed passwords. If it does, it executes the code in the main body of the program, and sends the result back to the user.
On a related topic, the other answers suggest using the md5 algorithm. However, this is not the most secure algorithm - while secure enough for many purposes, the hashlib module in the standard library gives other, more secure algorithms, and there is no reason not to use these instead.
You can check the hash of what a user has entered vs the hash of your password to check if the user has entered the correct password, I have made a very simple example to show this:
""" Python Password Check """
import hashlib
import sys
password = "2034f6e32958647fdff75d265b455ebf"
def main():
# Code goes here
print "Doing some stuff"
sys.exit(0)
while True:
input = raw_input("Enter password: ")
if hashlib.md5(input).hexdigest() == password:
print "welcome to the program"
main()
else:
print "Wrong Password"
In the example the hashed password is "secretpassword" which hashes to "2034f6e32958647fdff75d265b455ebf" so as you can see even if the source code is decompiled you can still only see the hash of the password rather than the plan text of the password.
To give this a bit of an update for 2016, currently if your hashing passwords in python you should be looking at one of the three following libs:
passlib
>>> # import the hash algorithm
>>> from passlib.hash import sha256_crypt
>>> # generate new salt, and hash a password
>>> hash = sha256_crypt.encrypt("toomanysecrets")
>>> hash
'$5$rounds=80000$zvpXD3gCkrt7tw.1$QqeTSolNHEfgryc5oMgiq1o8qCEAcmye3FoMSuvgToC'
>>> # verifying the password
>>> sha256_crypt.verify("toomanysecrets", hash)
True
>>> sha256_crypt.verify("joshua", hash)
False
Example lifted from here
bcrypt
import bcrypt
password = b"super secret password"
# Hash a password for the first time, with a certain number of rounds
hashed = bcrypt.hashpw(password, bcrypt.gensalt(14))
# Check that a unhashed password matches one that has previously been
# hashed
if bcrypt.hashpw(password, hashed) == hashed:
print("It Matches!")
else:
print("It Does not Match :(")
django-scrypt
On a server only server administrators should have the right to change the code. Hence, to change the code you have to have administrator access, and if you do, then you can access everything anyway. :-)
The same goes for a client program. If the only security is the password check, you don't need to get around the password check, you can just read the data files directly.
In both cases, to prevent people that has access to the files from reading those files a password check is not enough. You have to encrypt the data.
Let's start with the basic now, shall we? While doing the login credential password encryption, we should always do one way encryption. One way encryption means, you can't decrypt the text once it's encrypted. There'a encryption called md5 which are only one way encryption.
There's already a library available for it in Python called hashlib.
From the python docs:
>>> import hashlib
>>> m = hashlib.md5()
>>> m.update("Nobody inspects")
>>> m.update(" the spammish repetition")
>>> m.digest()
'\xbbd\x9c\x83\xdd\x1e\xa5\xc9\xd9\xde\xc9\xa1\x8d\xf0\xff\xe9'
>>> m.digest_size
16
>>> m.block_size
64
More on this: http://docs.python.org/2/library/hashlib.html?highlight=hashlib#hashlib
To protect data stored on the client machine, you have to encrypt it. Period.
If you trust an authorized user, you can use a password-based encryption key (many other answers on Stack Exchange address this), and hope that he is smart enough to protect his computer from malware.
If you don't trust the authorized user (a.k.a. DRM) you are just plain out of luck -- find another project.;-)
One way would be to store the password in a hash form of any algorithm and check if the hash of the password given is equal to the stored password hash.
The second way might be to take a password like "cat" and convert them to ascii and and add them up and store the sum. Then you can compare the given password's ascii sum to the one you stored.
OR you can combine them both! Maybe also hash the ascii sum and compare the given pass word's ascii sun's hash.
These are the three ways I know at least. And you can use chr or ord default function in python to convert to and back repeatedly to ascii. And you can use hashlib to hash.
I have spend the last couple of days refining this and running it through password crackers and it seems to be holding up pretty strong.
Here is my code for you to look at:
import time
import os
import random
import string
passwordScore = 0
def optionOne():
global passwordScore
#Code for checking a password
os.system('cls')
print('Option One has been selected')
password = input('Please type in your password here: ')
#Password check begins
if (len(password) > 7) and (password.isspace() == False):
#Check for capitalisation
for p in password:
if p.isupper() == True:
passwordScore += 1
else:
pass
passwordScore += 2
for s in string.punctuation:
#Beginning test for special letters
for p in password:
if s == p:
passwordScore += 1
else:
pass
else:
pass
# Returning results to the user
if passwordScore >= 5:
print('Your password is safe enough to use')
time.sleep(2)
elif passwordScore == 3:
print('We believe your password could be safer')
time.sleep(2)
else:
print('Your password is not safe enough to use')
print('using this password may place your data at risk')
time.sleep(2)
def optionTwo():
#Code for creating a password at random
print('Option Two has been selected')
chars = string.ascii_uppercase + string.ascii_lowercase + string.digits + string.punctuation
size = random.randint(8, 12)
newPassword = ''.join(random.choice(chars) for x in range(size))
print(newPassword)
def start():
print('Option 1: Check my passsword')
print('Option 2: Create a password')
option = input('Please chose your option here [ENTER]: ')
if option == '1':
#Option 1 has been selected
return optionOne()
elif option == '2':
#Option 2 has been selected
return optionTwo()
else:
#An error has occured
print('You have not selected a valid option')
time.sleep(1)
os.system('cls')
return start()
for i in range(1):
start()
This should do the job for almost everything as long as you tweak it to your needs!!
I have been looking through ths hashlib documentation but haven't found anything talking about using salt when hashing data.
Help would be great.
Samir's answer is correct but somewhat cryptic. Basically, the salt is just a randomly derived bit of data that you prefix or postfix your data with to dramatically increase the complexity of a dictionary attack on your hashed value. So given a salt s and data d you'd just do the following to generate a salted hash of the data:
import hashlib
hashlib.sha512( s + d ).hexdigest()
See this wikipedia article for more details
Just add the salt to your sensitive data:
>>> import hashlib
>>> m = hashlib.sha512()
>>> m.update('salt')
>>> m.update('sensitive data')
>>> m.hexdigest()
'70197a4d3a5cd29b62d4239007b1c5c3c0009d42d190308fd855fc459b107f40a03bd427cb6d87de18911f21ae9fdfc24dadb0163741559719669c7668d7d587'
>>> n = hashlib.sha512()
>>> n.update('%ssensitive data' % 'salt')
>>> n.hexdigest()
'70197a4d3a5cd29b62d4239007b1c5c3c0009d42d190308fd855fc459b107f40a03bd427cb6d87de18911f21ae9fdfc24dadb0163741559719669c7668d7d587'
>>> hashlib.sha512('salt' + 'sensitive data').hexdigest()
'70197a4d3a5cd29b62d4239007b1c5c3c0009d42d190308fd855fc459b107f40a03bd427cb6d87de18911f21ae9fdfc24dadb0163741559719669c7668d7d587'
Salting isn't a magical process that the library needs to help you with—it's just additional data provided to stop rainbow tables from working.
>>> import hashlib
>>> m = hashlib.sha512()
>>> m.update(b"Nobody inspects")
>>> m.update(b" the spammish repetition")
>>> m.digest()
b'\xd0\xf4\xc1LH\xadH7\x90^\xa7R\x0c\xc4\xafp\x0fd3\xce\t\x85\xe6\xbb\x87\xb6\xb4a|\xb9D\xab\xf8\x14\xbdS\x96M\xdb\xf5[A\xe5\x81+:\xfe\x90\x89\x0c\nM\xb7\\\xb0Cg\xe19\xfdb\xea\xb2\xe1'
>>> m.update(b"My super-secret salt.")
>>> m.digest()
b'\xcd\xd7K\xd9!~\xa8\x1d6\x9b\xa6\xde\x06\t\x02\xa1+}\xaeNA\x94a`\xaa\xf4\xe9\xb5\xff\x1f\x9cE\x84m\xbb\x98U\xb4z\x92\x9e\xe8\xc9\xc2\xc8\x8f\x068e\xb0\r\xed\xb7\xde\x80\xa6,\n\x111w{\xa2\x9b'
If you're looking for a replacement for crypt(), newer versions of glibc have SHA-512-based "$6$" with a variable iteration count (see Ulrich Drepper's page, which has a description and links to a complete C implementation of sha512_crypt_r()).
Writing your own crypto is highly unadvisable — the above sha512(salt+password) doesn't help against a brute-force attack.
For generating salt, use something like os.urandom(16) for random bytes or ''.join(map(lambda x:'./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[ord(x)%64], os.urandom(16))) for random base64-alike chars (for use with crypt()-alikes).
(I say base64-alike it's not the same as the Base64 in PEM/MIME.)
use passlib, writing your own password crypto is an almost sure way to failure.
SHA512 isn't a great way to store hashed passwords these days. You should be using bcrypt or something similar. What's important is that salting is built in and that the algorithm has a significant work factor.
If you salt your SHA512 passwords by simply appending (or prepending) the salt to the plaintext, anyone who gets their hands on a set of your hashed passwords and applies a modern cracking tool (http://arstechnica.com/security/2013/05/how-crackers-make-minced-meat-out-of-your-passwords/) will be able to see the concatenated password+salt values and will probably, through trivial pattern matching, be able to separate the password portion from the salt portion for most if not all of the accounts in question.
I haven't thought this through all the way, and I am by no means a security expert, but it seems to me that if you were to encrypt (using, for example, AES256) the password using the salt as the key, and then hash that with SHA512, you'd be safe from the vulnerability I described above.
However, at that point you've put in more effort than it would have taken to switch to bcrypt and you still wouldn't have the protection of a work factor, so I would only recommend an approach like that if the environment you're working in does not offer that option.
yes yes if my password is "pass" and my salt is "word"
my pass+salt is "password" same as just use password xD
or we use very secure crypth that safe salt to hashed output lol.
we just strip salt and generate hash with random passwords when we got same hash we got password lol lol