Re: [web2py] How to check if plain text password matches with encrypted password in auth_user.password?

2,062 views
Skip to first unread message

Jonathan Lundell

unread,
Dec 21, 2012, 8:25:00 PM12/21/12
to web...@googlegroups.com
On 21 Dec 2012, at 5:12 PM, Pearu Peterson <pearu.p...@gmail.com> wrote:
> Hi,
>
> I have a password in plain text and I want to check if it matches with the crypted password in auth_user.password field.
>
> I have tried comparing auth_user.password with str(db.auth_user.password.validate(plain_password)[0]) with no success even when I know that the passwords match exactly.
>
> The problem seems to boil down to the fact that encryption of the same string results different encrypted strings. For example,
> >>> from gluon.validators import CRYPT, LazyCrypt
> >>> crypt = CRYPT()
> >>> str(LazyCrypt(crypt, 'mysecret'))
> 'pbkdf2(1000,20,sha512)$a2a2ca127df6bc19$77bb5a3d129e2ce710daaefeefef8356c4c827ff'
> >>> str(LazyCrypt(crypt, 'mysecret'))
> 'pbkdf2(1000,20,sha512)$a555a267249876fb$bc18f82b72a3a5ebce617f32d6abaa5c48734ab9'
>
> What would be the correct way to check if passwords match when they are given in encrypted form?

You have to compare using the equality test in CRYPT, so compare CRYPT()(plaintext) == store_password (or something like that). You can't compare the resulting strings, because they have different (random) salt, at least by default they do.

>
> Any hints are appreciated,
> Pearu
>
>
> --
>
>
>


Pearu Peterson

unread,
Dec 21, 2012, 8:37:21 PM12/21/12
to web...@googlegroups.com
Thanks for the hint!

The following expression works for me:

  db.auth_user.password.validate(plain_password) == (db(db.auth_user.id==auth.user_id).select ().first ().password, None)


Thanks again,
Pearu

Jonathan Lundell

unread,
Dec 21, 2012, 10:31:38 PM12/21/12
to web...@googlegroups.com
On 21 Dec 2012, at 5:37 PM, Pearu Peterson <pearu.p...@gmail.com> wrote:
Thanks for the hint!

The following expression works for me:

  db.auth_user.password.validate(plain_password) == (db(db.auth_user.id==auth.user_id).select ().first ().password, None)

Good. There's probably a shortcut in Auth, too, that would make it less verbose.

A word to other readers: this is why the new format for stored passwords identifies the hash algorithm and includes the salt, which is (by default, anyway) randomly generated. When the user enters the plain password to log in, CRYPT can extract the algorithm and salt from the stored hash and hash the plaintext password with them, and then compare the result.

In Pearu's test above, the == operation is implemented by CRYPT.__eq__ (actually LazyCrypt.__eq__, but it amounts to the same thing), using the approach I described. It's in gluon/validators.py if you're interested in the details.

A nice side effect is that, if the comparison succeeds, you can migrate an old has to a newer, stronger one. That's pretty nice: you don't have to leave your MD5-hashed users hanging out to be hacked if someone manages to discover your hashes. (That's also a reason to use a global key for your hashes, which is not stored in the database, and not visible to someone who just manages to hack the database.)

Massimo Di Pierro

unread,
Apr 6, 2013, 12:28:28 PM4/6/13
to web...@googlegroups.com
This was explained many times before. You should look into the docstring of the CRYPT validator for examples and explanations. The bottom line is that

db.auth_user.password.validate(...) calls a crypt validator which returns (lazy_crypt(...), None or  'error')

The lazy_crypt object is not a string but it can be be comparer with a string and serialized into a string.

lazy_crypt(...) == 'hashed.... password'  reads the salt from the right hand side in order to perform a comparison.

Massimo


On Friday, 5 April 2013 14:02:39 UTC-5, Orrù wrote:

suppose password='12345' and db.auth_user.first_name=='Lucas'
so i find user by first_name,
row_user=db(db.auth_user.first_name=='Lucas').select().first()
and
row_user.password='pbkdf2(1000,20,sha512)$97448b22487eca1d$dae65c0429430b7ae7bb311fed8e844b6a37ff30'

db.auth_user.password.validate('12345') == (db(db.auth_user.id==row_user.id).select ().first ().password, None)
return False
CRYPT()('12345')==(row_user.password,None)
also returns false

where I am going wrong?

On Friday, December 21, 2012 11:12:26 PM UTC-2, Pearu Peterson wrote:
Hi,

I have a password in plain text and I want to check if it matches with the crypted password in auth_user.password field.

I have tried comparing auth_user.password with str(db.auth_user.password.validate(plain_password)[0]) with no success even when I know that the passwords match exactly.

The problem seems to boil down to the fact that encryption of the same string results different encrypted strings. For example,
>>> from gluon.validators import CRYPT, LazyCrypt
>>> crypt = CRYPT()
>>> str(LazyCrypt(crypt, 'mysecret'))
    'pbkdf2(1000,20,sha512)$a2a2ca127df6bc19$77bb5a3d129e2ce710daaefeefef8356c4c827ff'
>>> str(LazyCrypt(crypt, 'mysecret'))
    'pbkdf2(1000,20,sha512)$a555a267249876fb$bc18f82b72a3a5ebce617f32d6abaa5c48734ab9'

What would be the correct way to check if passwords match when they are given in encrypted form?

Vicente Orru

unread,
Apr 6, 2013, 2:15:15 PM4/6/13
to web...@googlegroups.com
thank you.


2013/4/6 Massimo Di Pierro <massimo....@gmail.com>
--
 
---
You received this message because you are subscribed to a topic in the Google Groups "web2py-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/web2py/eqbXmseZ6XA/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to web2py+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Reply all
Reply to author
Forward
0 new messages