if I create a hash using django's django.contrib.auth.hashers.make_password of the same string I get different hash every time. I don't understand how is this legal because as far as I know, hash functions must generate the same hash every time since by definition its a function. What am I missing?
from django.contrib.auth.hashers import make_password
password = "helloworld"
h1 = make_password(password)
h2 = make_password(password)
print h1, h2
h1 = u'pbkdf2_sha256$20000$Tr6NV5MewXYl$X+sezT6WRqBwYmJR/RZmZHLP6/l6ntSaBke0RKU1/v0='
h2 = u'pbkdf2_sha256$20000$05rEmxChtXlI$NdZGfTKH+kqt1viuFng3GmvBp6eJcsstxV4JcDlBGIs='
I suspect that different algorithms are used to hash every time and hence the hash is also different. Am I correct?
You see different results because of the salt. In simple words Django add some random string to the password before hashing to get different values even for same password. This makes rainbaw tables attack are useless. Actually what you see in DB is not plain hash value, it's structure in following format: <algorithm>$<iterations>$<salt>$<hash>
Each time you use make_password, the password is hashed with a different salt. Django stores the salt with the hashed password. You can then use check_password to check the password later.
from django.contrib.auth.hashers import check_password, make_password
password = "helloworld"
h1 = make_password(password)
check_password(password, h1) # returns True
check_password("incorrect", h1) # returns False
Read the docs on how Django stores passwords for more info.
Related
I am using the following code
email = validated_data["login"]
password = validated_data["password"]
user_obj = User.objects.filter(Q(email__exact=email) & Q(password__exact=password))
I changed the password from admin however no user is returned. However if I remove the password check then I get a user object back.The object that I get back if I remove the Q(password__exact=password) condition has _password field as None. This code has been working fine for a while but today it is not returning back the object. Am I missing something here ? I verified that I am receiving the correct username and password from the client.I also tried accessing the admin with that username and password (The account has staff status) and I was able to log in. So the password is correct but for some reason I cant obtain that user by filtering. ? What might I be doing wrong ?
password isn't stored in plain text, but as a hash (and a little more). Get the user by username and check the password:
# assumes there can be only one
user = User.objects.get(email=email)
# this checks the plaintext password against the stored hash
correct = user.check_password(password)
BTW, you don't need Q objects for logical AND. filter(email__exact=email, password__exact=password) would suffice, even though it doesn't make much sense, in this case.
it is because Django doesn't stores password as the simple text they are hashed, you cant perform a password__exact on that it will return none every time unless you are getting the same hash password = validated_data["password"] here
I have exported database from Discourse. it does contain password_hash and salt.
I have done my research and found out that Django uses PBKDF2 by default and even Discours use that with hashing algorithm sha256 and number of iterations 64000.
I want to migrate those password so that Django will be able to authenticate a user with the same password.
There's a number of ways you could do this.
Write your own authentication method in the backend - which accepts the same hashing method as Discourse when a user attempts to login. This way the hashed password should match from the user's salt and the password they have entered.
This can be done as follows:
from django.contrib.auth.hashers import PBKDF2PasswordHasher
class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher):
"""
A subclass of PBKDF2PasswordHasher that uses 64000 times more iterations.
"""
iterations = PBKDF2PasswordHasher.iterations * n
iterations = 64000 #Use this for simplicity!!
in hashers.py. Please note - PBKDF2PasswordHasher.iterations * n will have to equal 64000 - I think the number of iterations is currently set to 150000, so probably easier to have iterations = 64000 directly. The iterations is all you're looking to change, and all other behaviour will be inherited from the PBKDF2PasswordHasher Class.
Then, all you will need is:
PASSWORD_HASHERS = [
'application_name.hashers.MyPBKDF2PasswordHasher',
]
in settings.py, where application_name is, yep you guessed it, the name of the application where hashers.py can be found.
However...the following documentation on storage and hashing of passwords may be extremely useful in your search:
https://docs.djangoproject.com/en/2.1/topics/auth/passwords/#auth-password-storage
I want to send the email to the user that will contains url+hash
like this bleow
www.mywebsite.com/user/verify/121#$%3h2%^1kj3#$h2kj1h%$3kj%$21h
and save this hash against the user in the Database like this
ID | Email |Hash
1 | youremail#gmail.com |121#$%3h2%^1kj3#$h2kj1h%$3kj%$21h
When the user received the email it should check and compare the hash with it and perform the action as per situation.
My question is simple how to generate a unique hash for each user and how to store them in the Database.
If by "hash", you mean a unique string, you can just use uuid.uuid4 for that purpose.
>>> import uuid
>>> unique_id = str(uuid.uuid4())
>>> print unique_id
d8814205-f11e-46e1-925e-a878fc75cb8d
>>> # replace dashes, if you like
>>> unique_id.replace("-", "")
I've used this for projects where I need to verify a user's email.
P.S.: It's not called a hash, it's called a unique ID. Hashing is something else, where you generate a value from a given string. See this question for more explanation.
Django has a Cryptographic Signing module, which helps produce unique and verifiable signatures for any data you need. If you are trying to do this to verify that the request is done by the appropriate user or not, you can use the library to verify requests, without storing the hash in the database.
I have passwords stored in web2py using SHA 512 algorithm. I am now migrating the models to django and hence need a way to hash passwords in django using SHA 512 in the same way as web2py does so that I can authenticate the old users with the same passwords.Please suggest some way.
According to this post a Python snippet to recreate the convention used in web2py would be the following:
from hashlib import md5
import hmac
hmac_key = '<your secret key>'
password = 'insecure'
thehash = hmac.new(hmac_key, password).hexdigest()
print thehash
web2py uses hmac (which is your secret + the plaintext of the user's password) as the final hash and not just a straight MD5/SHA hash (depending on your settings). So you would just need to swap out MD5 for SHA in the above example to get things working on your end. But this implementation is all you would need to implement in your new application to make them cross compatible as long as the secret key is the same.
According to the docs the hash is stored in the following format:
<algorithm>$<salt>$<hash>
so if there is a salt used then it's stored with the hash making it easy to grab the salt for use in your new application. The dollar signs make it easy to parse each value.
algo, salt, hash = password_hash.split("$")
UPDATE: I pulled the below code from the web2py source but what you need to do is update the variable hmac_key with the value that you have set for auth.settings.hmac_key. Hopefully when you run (after you update the hmac_key variable) this the hashes should match.
import hashlib
import hmac
from hashlib import sha512
h="sha512$b850ed44943b861b$c90901439983bce7fd512592b20d83f8e654632dee51de515773e70eabe609f62cebec64fed4df03acd54e6a627c9291e70fdf3a89996ffa796897c159e95c11"
algo,salt,hash = h.split("$")
print "crypted hash: %s"%hash
pwd = "pawan123"
##get this value from auth.settings.hmac_key
hmac_key = ""
def get_digest(value):
"""
Returns a hashlib digest algorithm from a string
"""
if not isinstance(value, str):
return value
value = value.lower()
if value == "md5":
return md5
elif value == "sha1":
return sha1
elif value == "sha224":
return sha224
elif value == "sha256":
return sha256
elif value == "sha384":
return sha384
elif value == "sha512":
return sha512
else:
raise ValueError("Invalid digest algorithm: %s" % value)
#hashed = simple_hash(self.password, key, salt, digest_alg)
def simple_hash(text, key='', salt='', digest_alg='md5'):
"""
Generates hash with the given text using the specified
digest hashing algorithm
"""
if not digest_alg:
raise RuntimeError("simple_hash with digest_alg=None")
elif not isinstance(digest_alg, str): # manual approach
h = digest_alg(text + key + salt)
elif digest_alg.startswith('pbkdf2'): # latest and coolest!
iterations, keylen, alg = digest_alg[7:-1].split(',')
return pbkdf2_hex(text, salt, int(iterations),
int(keylen), get_digest(alg))
elif key: # use hmac
digest_alg = get_digest(digest_alg)
h = hmac.new(key + salt, text, digest_alg)
else: # compatible with third party systems
h = get_digest(digest_alg)()
h.update(text + salt)
return h.hexdigest()
print "result hash: %s"%simple_hash(pwd, hmac_key, salt, "sha512")
I think your best solution is to write an auth backend that will authenticate the User against the web2py base, then ask him to change or confirm his password and build a new Django auth-passwords.
The hole idea of crypto-hashing passwords is that you or any hacker can't see them if you have access to the database.
Here is the Django documentation on writing an authentication backend.
I create a user in my view.py using this simple code.
if not errors:
user = User.objects.create_user(username, email, password)
user.save()
Except for the validation, there is nothing that I do to the username and password values before creating the object.
But I find this in the User class in Django API. I don't know how to use the help text. If it is help text what does it print? How do I find the default values of algo, salt and hexdigest?
password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the change password form."))
"If it is help text what does it print?"
-> it prints exactly this: Use '[algo]$[salt]$[hexdigest]'
when you create a user, it will automatically call make_password(password[, salt, hashers])
which: Creates a hashed password in the format used by this application. It takes one mandatory argument: the password in plain-text. Optionally, you can provide a salt and a hashing algorithm to use, if you don't want to use the defaults (first entry of PASSWORD_HASHERS setting). Currently supported algorithms are: 'pbkdf2_sha256', 'pbkdf2_sha1', 'bcrypt' (see Using bcrypt with Django), 'sha1', 'md5', 'unsalted_md5'
are you facing any problems with this?
create_user will automatically generate password hash and it will create user in the database (thus you don't need that user.save())
See docs on creating users.
The help text is basicly just code for the message that shows up in the django admin, when editing a User object. It's meant to explain to someone looking at the edit form, why the password field has something like sha1$12345$1234567890abcdef1234567890abcdef12345678 instead of the password that was set for that user. The reason is, of course that the password is hashed for security, and that representation holds all the information required to verify a user-typed password later.
The admin user edit form has a special page for editing passwords. If you want to edit the users password in your code use the set_password method of the User object, the check_password method is for verifying a supplied password.
The documentation for make_password has more information about the algorithms Django uses and can use. The default for Django <1.3 was sha1, Django 1.4 changed the default to PBKDF2. The default value for salt is a random string (it's there so that two identical passwords don't look the same in the database). Hexdigest is the value of the password string and the salt string hashed with the hashing algorithm. You can read the details in the code on github.