simple_hash and salt order

38 views
Skip to first unread message

David Orme

unread,
May 26, 2016, 1:19:35 PM5/26/16
to web2py-users
Hello,

I've got an application where I'm sharing a database with a second (non web2py) framework. I want my web2py application to handle user registration and would like to avoid users having two passwords (partly so that only web2py ever writes to the auth_user table).

Inevitably, the hashed password storage formats differ, but I can match the hash algorithm between the two frameworks:

db.auth_user.password.requires = CRYPT(digest_alg='sha512')

Then I can just calculate the value of a second hashed password field in the foreign format - it involves recoding the string as base64, not hex, but that can be achieved using a computed field.

def alt_password(r):
    passwd
= r.password.split('$')
    alt
= base64.b64encode(passwd[1].decode('hex')) + \
               
'*' + base64.b64encode(passwd[2].decode('hex'))
   
return alt


auth
.settings.extra_fields['auth_user']= [
   
Field('alt_password', compute=lambda r: alt_password(r))
   
]



Except... the simple_hash function in web2py uses (password + salt) as an input and the second framework uses (salt + password), which means there is no way to reproduce the second format from the stored hashed password. I can hack the web2py utils.py file on my installation to reverse this but I wanted to check if there was a more elegant way of overloading the simple_hash function without having to change the codebase, which makes my application unstable to upgrade.

I did wonder about extending the settings to include a salt order, but I think that would mean you'd have to extend the password string to record the order: alg$order$salt$hash. That seems like a bit of a big change for a fairly fringe use case!



Anthony

unread,
May 27, 2016, 12:51:40 AM5/27/16
to web2py-users
Aside from forking the framework, I suppose you could take one of these approaches:
  1. Monkey patch gluon.utils.simple_hash.
  2. Subclass validators.CRYPT and validators.LazyCrypt, and in LazyCrypt, replace the __str__ method with one that calls a custom simple_hash function.
  3. Create an entirely new custom hashing validator that replicates the algorithm used by the other app.
Anthony

David Orme

unread,
May 27, 2016, 6:47:32 AM5/27/16
to web2py-users
Hi Anthony,

Many thanks. Just to check - option 1 would have to be done in the web2py codebase (i.e. outside of the application code)? I don't think there's any simple way of overriding the version of simple_hash imported from gluon.utils that LazyCrypt() calls.

Both the other options can be done in a model file, I think. 

Cheers,
David

Anthony

unread,
May 27, 2016, 10:18:11 AM5/27/16
to web2py-users
On Friday, May 27, 2016 at 6:47:32 AM UTC-4, David Orme wrote:
Hi Anthony,

Many thanks. Just to check - option 1 would have to be done in the web2py codebase (i.e. outside of the application code)? I don't think there's any simple way of overriding the version of simple_hash imported from gluon.utils that LazyCrypt() calls.

Actually, you have to monkey patch simple_hash in gluon.validators, but you can do it in your application code (e.g., in a model file):

def my_hash(*args):
   
...
   
return hash

import gluon.validators
gluon
.validators.simple_hash = my_hash

Of course, once that code has been run, it will affect all applications until you restart the server. Also, since it really only needs to be run once (not on every request), you could put it in routes.py, which is only run when the server starts (or when you explicitly reload it).

Anthony
Reply all
Reply to author
Forward
0 new messages