Yeah, that's definetely a mistake! The hash needs to be generated using both salt and password. Also, I saw a technique when you generate a hash using double hashing, like this: sha(sha(password) + salt).hexdigest(). It looks even more secure :) BTW, to fix it, we need to remember about migration of already stored hashes. I guess zope.app.generations will do the job. 2009/1/17 Uli Fouquet <uli@gnufix.de>:
Hi there,
while working on a password manager tool (commandline) for Grok I stumbled over the usage of salts in the password managers of `zope.app.authentication`.
In short, they seem to generate (and store) a salt number but do not make any use of it when it comes to creating the hashes (SHA1, MD5, whatever). As a result, same passwords lead always to same hashes, only the leading salt number is different. This could be exploited by dictionary attacks.
The zope.app.authentication implementation for SHA1-encoding looks roughly like this (other hash-encodings have the same problem, if this is a valid problem)::
def encodePassword(password, salt=None): if salt is None: salt = "%08x" % randint(0, 0xffffffff) return salt + sha.new(password).hexdigest()
It looks to me that this way `salt` is only an arbitrary string that gets stored in front of the real hash value. The hash value itself will not change with the salt.
When I run the following code (`checkPassword` like in `zope.app.authentication.password`)::
def checkPassword(storedPassword, password): salt = storedPassword[:-40] return storedPassword == encodePassword(password, salt)
for i in range(0,3): enc = encodePassword('asd') print 'Encode "asd"', enc print 'Check: ', checkPassword(enc, 'asd')
I get::
Encode "asd" 81bde2dbf10e2821bbbea527ea02200352313bc059445190 Check: True Encode "asd" c96cfabdf10e2821bbbea527ea02200352313bc059445190 Check: True Encode "asd" bdba5b69f10e2821bbbea527ea02200352313bc059445190 Check: True
As you can see, only the first eight letters change. And they are cleartext part of the encoded password. Not a problem for an dictionary-attacker: if one of the hashes in his/her dictionary are equal to ``f10e2821bbbea527ea02200352313bc059445190``, then the password is cracked. Salts do not improve the security level here, I think.
What I assume to be correct, would be to build the hash from the salt plus password (instead of only the password)::
def encodePassword(password, salt=None): if salt is None: salt = "%08x" % randint(0, 0xffffffff) return salt + sha.new(salt + password).hexdigest()
Using this modified function, I get::
Encode "asd" 11ac348fe526cc38813fca0e5bd0a59ec3a16686bfa42502 Check: True Encode "asd" 08de8fa19212d743867f8867adee55a9efbe566a8ec56731 Check: True Encode "asd" d454b892224b0cf5b41767acfa80a3732b82c52fc2ee5e9f Check: True
Now it is harder for an attacker. His dictionary has not only to provide the pure hashes for every entry in the dictionary, but also 16**8 variants for _each_ of the entries. That's it, I think, what salt is used for.
As I am not a computer scientist nor a mathematician, I am not sure, whether these are valid concerns. But I would like to know, what others think about this. Maybe you can correct me here.
Best regards,
-- Uli
_______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org http://mail.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - http://mail.zope.org/mailman/listinfo/zope-announce http://mail.zope.org/mailman/listinfo/zope )
-- WBR, Dan Korostelev