I have an old database where user passwords were hashed with md5 without salt. Now I am converting the project into django and need to update passwords without asking users to log in.
I wrote this hasher:
from django.contrib.auth.hashers import PBKDF2PasswordHasher
class PBKDF2WrappedMD5PasswordHasher(PBKDF2PasswordHasher):
algorithm = 'pbkdf2_wrapped_md5'
def encode_md5_hash(self, md5_hash, salt):
return super().encode(md5_hash, salt)
and converting password like:
for data in old_user_data:
hasher = PBKDF2WrappedMD5PasswordHasher()
random_salt = get_random_string(length=8)
# data['password'] is e.g. '972131D979FF69F96DDFCC7AE3769B31'
user.password = hasher.encode_md5_hash(data['password'], random_salt)
but I can't login with my test-user.
any ideas? :/
I'm afraid you cannot do what you want with this. Hashing is strictly one-way, so there is no way to convert from one hash to another. You WILL have to update these passwords to the new hash one-by-one as users log in.
A decent strategy for implementing this change is:
Mark all of your existing hashes as md5. You can just use some kind of boolean flag/column, but there is an accepted standard for this: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
When the user logs in, authenticate them by first checking which type of hash they have, and then calculating that hash. If they are still md5, calculate the md5 to log them in; if they are now using pbkdf2, calculate that hash instead.
After authenticating the password, if they are still flagged as md5, calculate the new format hash and replace it - making sure to now flag this as pbkdf2.
IMPORTANT: You will want to test this thoroughly before you release it to the wild. If you make a mistake, you might destroy the credentials of any user logging in. I would recommend temporarily retaining a copy of the old md5 hashes until you confirm production is stable, but make absolutely certain you destroy this copy completely. Your users passwords are not safe as long as the md5 hashes exist whatsoever.
Related
I am making a Django project that will be hosted locally in different environments.
I want users to be able to login by just entering a six-digit PIN on a touch screen or keyboard instead of having to type out a lengthy username/password.
I need to store a PIN for users in the DB. I want the PIN to be hashed or encrypted in some way so that it is not visible in the database. The PIN (and therefore its hash) must be unique but it also must be converted to the same value each time. For instance, every time 123456 is entered it needs to be converted to "jhs8d67RandomString34kds" so that no two users can save the same PIN as the DB column will be unique.
I need to know how to change a user-entered integer and hash it to save in the database.
Then I need to know how to compare it when a user enters the PIN.
I really need some examples on how to implement this and not a lesson in telling me why this is "insecure" or won't work.
Any ideas would be greatly appreciated.
Hashing something doesn't make it secure
All hash function have clashes, the only difference is the probability
Integers have hash function implemented, just use that
Note that for security reasons hashing for strings in randomized in each python process. so those hashes cannot be used for persistent data
You can use module-hashlib:
import hashlib
pincode = "123456"
hashlib.md5(pincode).hexdigest()
'e10adc3949ba59abbe56e057f20f883e'
And the to compare you can do the same:
if hashlib.md5(pincode).hexdigest() == 'e10adc3949ba59abbe56e057f20f883e':
you code here
...
Or use hashlib.pbkdf2_hmac with salt:
hashlib.pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None)
import hashlib
dk = hashlib.pbkdf2_hmac('sha256', b'password', b'salt', 100000)
dk.hex()
'0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5'
I found the Python package to encrypt some data and see this in python Cryptography:
It is possible to use passwords with Fernet(symmetric key). To do this, you need to run the password through a key derivation function such as PBKDF2HMAC, bcrypt or scrypt.
But, it turns out that a password works in the same way as a key(use password/key to en/decrypt). So why bother to use password instead of key itself?
I mean why not just use key itself:
from cryptography.fernet import Fernet
key = Fernet.generate_key()
token = Fernet(key).encrypt(b"my deep dark secret")
Fernet(key).decrypt(token)
A password is something that can be remembered by a person whereas a key is usually not remembered, because it is long (at least 128 bit or Hex-encoded in 32 characters) and is supposed to be really random (indistinguishable from random noise). If you want to encrypt something with a key, but this key cannot be transmitted by asymmetric cryptography and instead should be given over the phone or should never be written anywhere, then you can't simply generate a random key and use that. You need have a password/passphrase in place to derive the key from.
Example 1:
A personal password safe like KeePass needs a key for encryption/decryption. A user will not be able to simply remember that key, therefore we have a much shorter password, which can be remembered. Now, the security lies in the fact that a slow key derivation function is used to derive a key from the password, so an attacker still has trouble brute-forcing the key even though it depends on a much shorter password.
Example 2:
You compress a document and use the encryption of the compression software. Now you can send the container via e-mail, but you can't send the password along with it. So, you call the person you've sent the e-mail to and tell them the password. A password is much easier to transmit than a long and random key in this way.
I'm using WTForms-Alchemy to define forms from model objects. I defined a field as a password thus:
password = db.Column(PasswordType(schemes=['pbkdf2_sha512']), nullable=True)
I persist the form to PostgreSQL and I always end up with the wrong hash in the database. Interestingly, this method worked flawlessly on a previous project that used MySQL.
I've now decided to encrypt my passwords by hand by calling pbkdf2_sha512.encrypt and pbkdf2_sha512.verify manually and the hashes are stored correctly.
Am I missing a configuration parameter? Could this be a bug?
I'm not entirely sure what the issue here is, but I wanted to mention that using pbkdf2 is not recommended now-a-days -- if you're storing user password hashes you should preferably be storing passwords using bcrypt. bcrypt is a cpu hard hashing algorithm, which makes it much harder to brute force for potential attackers.
So an emergency project was dumped on me to merge a MySQL user database into an existing Django user database.
I've figured just about everything out except how to handle the passwords as they use different hashes. I don't know Python, the Django backend, or very much about hashing techniques.
I do have a way to verify users with their emails, I just need a way to take the passwords they give me and save them into the database in a Django-acceptable way. It will be have to be done in Perl since that's the only language I know on the server.
I found this page talking about how Django handles passwords, but I sadly don't understand most of what they're saying. Also, I don't know if it's any help, but the admin area of the Django site gives the "hint" of
"Use '[algo]$[salt]$[hexdigest]'" for the password.
That doesn't mean much to me either, but maybe it does to one of you?
There are basically two ways to handle this: convert existing passwords to a format acceptable by Django, or write your own Django password hasher.
For the first way, as you found, the password field consists of three parts, each separated by a $. (Django 1.6 passwords may have 4 parts, but let's ignore that extra part for now, since Django 1.6 also supports the more traditional 3-part passwords.) The parts are
algorithm, which describes the password hashing algorithm; it will look like md5, pbkdf2, etc.
salt, the salt for the hash algorithm
hexdigest, the hashed password
So, assuming your passwords are already salted and hashed, your script needs to take the hashed/salted passwords in your existing database, separate the salt from the hash, then store them into the database with the appropriate algorithm string prefixed. There should be Perl modules for doing password hashing using various algorithms. Django's recommended algorithm is PBKDF2. bcrypt is also good. Any hash algorithm is fine, though, as far as Django is concerned, as long as it has a built-in hasher for that algorithm (Django has hashers for the most common hashing algorithms).
If your existing passwords are not salted and hashed…well, now would be a good time to do that. ;-)
The alternative way is to just copy the passwords over to the new database as-is, and write your own password hasher to handle them in your Django app. Of course, that would require writing some Python code.
I made a topic about the built-in python hash function: Old python hashing done left to right - why is it bad?
The previous topic was about why it was bad for encryption, because we have an application called Gruyere which is filled with security holes, and it uses the hash() to encrypt cookies.
# global cookie_secret; only use positive hash values
h_data = str(hash(cookie_secret + c_data) & 0x7FFFFFF)
c_data is a username; cookie_secret is salt (which is just '' by default)
I have implemented a more secure encryption method using md5 hashing with salt, but one excercise is to beat this old encryption and I still cannot understand how :-( I've read the string_hash code from python sourcecode but it's not documented and I can't figure it out.
EDIT: The idea is to write a program which can create a valid cookie any valid user, so I think I need to find out cookie_secret somehow
Zack described the answer already in your last question: It's easy to find a collision.
Let's say you save hash("pwd") in the database (that you actually do something different doesn't matter. Now, if you enter "pwd" in the site, you can enter. But how is this checked? Again, the hash of "pwd" is token, and compared to the value in the database. But what if there is a second string, say "hello", and hash("hello") == hash("pwd")? Then you could also use "hello" as password. So to beat the encryption, you don't need to find "pwd", you just need any string which has the same hash-value. You can just search for such a string brute-force (and I guess you can do some optimizations based on the knowledge of the source of hash)