[Zope-dev] Salt-weakness in zope.app.authentication passwordmanagers?

Uli Fouquet uli at gnufix.de
Sat Jan 17 12:01:34 EST 2009


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

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Dies ist ein digital signierter Nachrichtenteil
Url : http://mail.zope.org/pipermail/zope-dev/attachments/20090117/0e712892/attachment.bin 


More information about the Zope-Dev mailing list