Can't login after upgrading to 1.7.3, and workaround

218 views
Skip to first unread message

Horacio G. de Oro

unread,
Jan 14, 2015, 4:57:13 PM1/14/15
to django-d...@googlegroups.com
The problem is because the iterations in PBKDF2PasswordHasher where updated to 15000, so it updates the password, but later, SessionAuthenticationMiddleware detects a password change and PUF! the login doesn't work.

Right new I've created a PBKDF2PasswordHasher implementation with iteartions = 12000 (the old value), but I want to report the issue (I don't know if this is a bug) in case it happens to anyone else.

Saving the user returned by authenticate() before calling login() solved the issue too, but I dont' want to save the user just in case the password changed.

Regards!
Horacio


--

Tim Graham

unread,
Jan 14, 2015, 8:31:55 PM1/14/15
to django-d...@googlegroups.com
Hi Horacio,

Thanks for the report. Unfortunately, I can't reproduce this issue.

My steps to reproduce using the project from the tutorial:
Bump PBKDF2PasswordHasher.iterations to a higher value
Login at /admin/
Confirm at /admin/auth/user/#/ that the password of the user I logged in as reflects the new iteration count.

Maybe there is something different in your setup?

Horacio G. de Oro

unread,
Jan 15, 2015, 1:05:21 AM1/15/15
to django-d...@googlegroups.com
I haven't tested the issue outside my project. But while debugging, I saw the new hash in the user instance returned by authenticate(), but that new hash never went to the database since I haven't done a 'save()' of the user instance.

If you see the new hash in the database, I think that, maybe the admin, is saving the user instance with the new hash. And doing that, ie: save()'ing the user returned by authenticate() solved the issue for me too (but I don't want to save the user every time he/she logins, just in case the hasher parameters changed).

Horacio G. de Oro

unread,
Jan 15, 2015, 1:14:27 AM1/15/15
to django-d...@googlegroups.com
As part of the authentication process, I increment/reset authentication tries of the user. And to check this, I get the user from the DB before doing the authentication. I think this is causing me the issue. I reset the countre of login attempts in an instance of user that is different than the returned by authenticate. So, in the DB, the old hash is saved, but I return the user with the new hash.

Maybe authenticate() do the save() of the user? If that's true, I'm overwriting the new hash with the old one when resetting the counter...

Horacio G. de Oro

unread,
Jan 15, 2015, 1:19:25 AM1/15/15
to django-d...@googlegroups.com
I do something like.

    # get the user to check tries
    user = User.object.get(username=username)
    assert user.count <= 3

    # authenticate
    authenticated_user = authenticate(...)

    # auth failed
    if not authenticated_user:
        user.count++
        user.save()
        raise

    # auth ok
    user.count = 0
    user.save()   # <A>

    # return user annotated by authenticate()
    return authenticated_user


Maybe the problem is doing <A>.

Horacio G. de Oro

unread,
Jan 15, 2015, 1:31:17 AM1/15/15
to django-d...@googlegroups.com
Yeap. Removing the save() done in <A> solved the issue.

And some component is doing and update() - not a save(). I see in the Postgresql logs an UPDATE of the hash with the new increment, that includes only the "password" field.

I thought that autenticate() produced a read operation on the DB... now I know that it's read/write!

So, it's not a Django issue :-)

Thanks!
Horacio

Marc Tamlyn

unread,
Jan 15, 2015, 2:22:01 AM1/15/15
to django-d...@googlegroups.com
I think therefore you could resolve the issue by doing save(update_fields=['count']) in your code.

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/3cfce232-a561-4c79-8728-8f33548d2cf3%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Nic West

unread,
Jan 15, 2015, 4:03:40 AM1/15/15
to django-d...@googlegroups.com
Authenticated returns a user instance assumably with the correct hash why not do some thing like:

authenticated_user.count = 0
authenticated_user.save()

James Bennett

unread,
Jan 15, 2015, 5:27:42 AM1/15/15
to django-d...@googlegroups.com
On Thu, Jan 15, 2015 at 12:05 AM, Horacio G. de Oro <hgd...@gmail.com> wrote:
If you see the new hash in the database, I think that, maybe the admin, is saving the user instance with the new hash. And doing that, ie: save()'ing the user returned by authenticate() solved the issue for me too (but I don't want to save the user every time he/she logins, just in case the hasher parameters changed).

For sake of completeness, this is not specific to the admin. It's done by django.contrib.auth. The default ModelBackend for authentication will update the hashed password during any successful login, if either of these conditions is met:

1. The hashed in the password retrieved from the database used a hash algorithm other than the algorithm of the currently-preferred password hasher, or

2. The hasher indicates that the stored hashed password needs to be updated.

In the case of the PBKDF2 hasher, you ran into (2) -- that hasher checks whether the number of iterations used on the hashed password is different than the current specified number of iterations, and forces an update of the stored hashed password on a mismatch.

The update itself is done by the User instance, calling self.save() and specifying that only the 'password' field will be updated.

Horacio G. de Oro

unread,
Jan 15, 2015, 8:43:51 AM1/15/15
to django-d...@googlegroups.com
Yes, I implemented that fix last night, but I really like the Marc idea of doing save(update_fields=['count']) 

Thanks!

Florian Apolloner

unread,
Jan 26, 2015, 5:43:54 PM1/26/15
to django-d...@googlegroups.com
I can reproduce the issue:

Log a user into the admin
Change iteration count
Use a new browser (or private window) and log the same user into the admin
Refresh the admin page of the first user -> login window

Cheers,
Florian

Tim Graham

unread,
Jan 26, 2015, 5:48:19 PM1/26/15
to django-d...@googlegroups.com
I think that's a bit different from the originally reported issue, but it seems like a reasonable thing to fix if we can.
Reply all
Reply to author
Forward
0 new messages