Persistent Login (the “remember me” checkbox)
It is a requirement of many customer projects of mine to have a "remember me" or "keep me logged in" checkbox on the log-in form. I am not aware of any standard approach to the subject; I suspect every company invents their own (and then blogs about it?) Here's what I do when asked to implement this feature, as online on art.world.
Here are the requirements as I see them:
- Users should be able to use the "remember me" function from one computer (e.g. home) but not from others (e.g. internet cafe)
- Users should be able to use the "remember me" function from as many computers as they like (e.g. home, their phone, ..)
- Typing the user name, password and "remember me" should remember only this user name and password; thus, if they change their password using another computer, this "remember me" should stop working.
Here are some thoughts on various options:
- One possibility would be to store the user's password in a browser cookie. We shouldn't do that, because although stealing any form of persistent login cookie would mean the thief could log in, if the password is in plain text in the cookie then the thief could also do certain operations protected by the password such as delete account, change email address, password, etc.
- We store the password hashed with bcrypt in the database; one possibility would be to store the hashed password in a browser cookie. But then, if someone stole our database, they would have the bcrypt versions of every password in our database, they would be able to imitate any user by setting the bcrypt password in their cookies. That's why we use bcrypt at all, to prevent being able to immediately log in as any account if the DB gets stolen. (One could argue they have already stolen our db in this case, so the extra harm of imitating a user would arguably be marginal; but they might still be able to e.g. send messages to other users by using the site)
- We should not bcrypt the bcrypt and store that in the cookie. Because then, as above, anyone who stole our bcrypt passwords can just bcrypt them and again impersonate any user.
- If we generate any "code" or "token", we should store it bcrypted in the database. Otherwise, once again, any user who steals our db, has all tokens, and can impersonate any user.
- Because we store the token bcrypted, even we can't get back to the original token. The user must have "persistent login" capability from more than one computer. Therefore, we would need to store one "token" per user's computer. (Otherwise e.g. user logs on on computer A, token generated, stored in client, bcrypted stored in DB. User logs on on computer B, cannot send token to client, we don't know what it is. If we overwrote it, computer A couldn't log on any more).
So this is the approach we take:
If the user logs in and clicks "remember me" then we
Create a random:
client_id
(= representing their computer)- random token (= representing their authorization on this computer)
Set these in long-term cookies:
persistent_login_id
(the ID of the user trying to log in, or email address thereof, or whatever)persistent_login_client_id
persistent_login_token
Insert a new row to the new table
persistent_login
, with fields or similar with fields:login_id
client_id
token_bcrypt
is bcrypt of (current password in bcrypt form (it's the only form of the password we have at this point), and the token)last_used_utc
= now
If the user comes to our site and has no session or is logged out, and all three of these cookies are set, we:
- Find the row in the table for the (
login_id
,client_id
) as according to the cookies - Check to see if the token from the cookie is correct, against the bcrypted version in the row
- Log the user in silently if the values are correct
- Set
last_used_utc
in the db row to be now.
- Find the row in the table for the (
If the user changes their password, next time a computer attempts to log in using this mechanism, the
password_token_bcrypt
will not match, therefore this has the effect that changing the password invalidates all persistent logins across all devices. (Another alternative would be to to not use the password when hashing the token and simply delete all rows if the user changes their password; but I fear there will be a password change facility somewhere which will forget this deletion; with this approach there is nothing to remember to do and thus nothing to forget to do.)Cleanup process cleaning up rows which haven't been used e.g. in one month.
Here are some non-requirements as I see them, although various blogs and articles on the internet assert they're very important. I cannot see why they are important, but if anyone would enlighten me, I would be most appreciative.
- Time limited persistent logins
- One blog says, each time a persistent login is used, one should overwrite the cookie with a new persistent login. Can't really see what (realistic) attack that prevents us from.
Apologies for using the term "computer" in the above text rather than the more accurate term "client", but it seemed to make the whole thing easier to understand.
Further reading:
http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/
http://stackoverflow.com/questions/549/the-definitive-guide-to-forms-based-website-authentication#477579
https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence (he forgets to mention changing pw should invalidate the "remember me")