Correct way to "remember me"

Googled this a fair bit but there’s just too much conflicting information.

From what I’ve gathered, the BEST option is to, if checked, a authtoken key is made into a cookie and stored on the users computer. This cookie contains hashed / salted username / password along with an expiration date of when it’s no longer valid.

Upon login, if the user has that cookie and it matches up with the login details (unmodified cookie) then I log in. If the cookie is expired, I ignore / delete the cookie and only regenerate a new cookie if they select remember me again.

Is this correct?

There are plenty here with more experience than I but I would NOT save any part of their log in info to session.

I generate a session key and token when a person logs in which are stored in a cookie. I also store these two values along with user_id, secure_level, date_created to a DB table.

I generally set user_id and secure_level to session to validate user, so if these are NOT found, I look for the cookie and get the session key and token values. I use these to query DB for user_id and secure_level, and if found and within allowed (time from when record was added), I will update the cookie time and the DB table and set user_id and secure_level to session.

Anyway, that’s the way I’ve done it.

I think you’re referring to Cross-Site Request Forgery (CSRF) Attacks - more info can be found here OWASP among other things. The way I do it is just create a secure token (I use OPEN_SSL) every time a user logs in, Store the token in sessions then check against the form by using a hidden input field with the same token. There are many ways of doing this as you can see.

@oddz Could you explain more about how session keys / token values work in that system. Each login has a new key right? So I assume that is updated in the database accordingly, right? How do you keep track of a cookie made…like a week ago with a completely DIFFERENT key/token if you update the db value each time they login?

@Pepster yes, now the key is finding one that’s going to work for my system :slight_smile: .

I have created an openssl auth key. Is it really enough to just assign that to a cookie for remember me and query the database for the users details and then log in? Seems like it’d be easy to steal that cookie and get automatic access.

I was thinking of setting the cookies value to username(email)+authkey and then hash that somehow to even further obfuscate it.

Upon retrieving the cookie, I unhash it (or whatever), query the database for that username, see if the authkey matches, and if so, allow them in. Now that I’m typing it, it seems like this way is just a more unsecure version of my previous description. Hmm…

Anyway, thoughts appreciated.

I assume you are asking me. Yes I delete the record each time they log in.

"DELETE FROM user_sessions WHERE user_id = :user_id";

If they successfully log in I generate new key and token by calling these functions

/////////////////////////////////    
/////////////////////////////////    
function generateKey() {        
    // get the largest 999999999 we can rand to.
    $max = (int)str_pad('', strlen(mt_getrandmax()) - 1, 9);
    $min = (int)str_pad('1', strlen($max), 0, STR_PAD_RIGHT);

    $key = '';
    
    while (strlen($key) < 39) {
        $key .= mt_rand($min, $max);
    }
    
    return substr($key, 0, 39);
}
/////////////////////////////////    
function generateToken() {        
    // get the largest 999999999 we can rand to.
    $max = (int)str_pad('', strlen(mt_getrandmax()) - 1, 9);
    $min = (int)str_pad('1', strlen($max), 0, STR_PAD_RIGHT);
    
    $key = '';
    
    while (strlen($key) < 4) {
        $key .= mt_rand($min, $max);
    }
    
    return substr($key, 0, 4);
}
/////////////////////////////////    
/////////////////////////////////

I set the cookie like so.
NOTE: COOKIE_KEY is a constant I have set.

// Store key in cookie
setcookie(COOKIE_KEY, 
    "$Token|$key", 
    time() + (3600 * NUM_HOURS), "/");

I get the token and key from the cookie like so and use these values to get user_id and level.

if(isset($_COOKIE[COOKIE_KEY])):
    $parts = explode('|', $_COOKIE[COOKIE_KEY]);
    
    $Token = $parts[0];
    $key   = $parts[1];
endif;

Anyway, that’s what I came up with and there are many ways to do it. I knew I didn’t want the cookie to contain anything that could directly identify the user, i.e. email, username, user_id etc so I figured storing those values in the DB table would be better and using two different keys would give you pretty good security.

I hope I misunderstood that whole sentence, because, wIth a “remember me” system, there is no “further login”. Should the “remember me” cookie be available and valid, then the user is logged in automatically through the cookie. That is the whole idea of having that system. :smile: So, the sentence should read:

Upon entering a page usually needing a login, if the user has a remember me cookie available and its token can be used to authenticate the user, then the user is automatically logged in. If the cookie is expired, the user must login in again and if the “remember me” check box is checked, then a new “remember me” cookie is set.

:wink:

Did you decide to throw OOP out the window? Or is that just a snippet of a class? :smiley:

If I may make a similar suggestion to what someone else made. Put up a popular and modern framework of your choice next to your own work and reference to it. Try out your code/ methods and when you say, now I am stuck or now I am finished, dig into the framework and its documentation to see how they solved the same problem, because 99% of the time it is an accepted and correct way to solve the problem. Doing that will certainly give you a “push” in the right direction for sure, if you are stuck and will show you for sure, how you went wrong, if you went wrong, when you are finished.

You might also find decent books on the subject too. Like one I had the pleasure of working on.

This book goes into great detail about the Symfony security systems and you’ll learn quickly, user login and remember me cookies are just a small part of the bigger web application picture (and why frameworks are so valuable), when it comes to security.

Edit: I just realized, the suggestion to use a framework was made in another one of your topics.

Scott

1 Like

Yes, thank you for that explanation. That does help me out.

It’ll be more work for myself if I do it myself :slight_smile: . I want this project to last a while. Thank you though.

</thread>

Oh, most definitely try things out yourself. Making your own mistakes is the fastest way to learn. :smile:

Scott

Interesting Question. I would be interested if anyone can highlight any flaws with the way I’m doing it.

I have been generating a token then saving the password_hash to the database alongside the IP address. I’ll then check that using password_verify, if it makes regenerate a new token.

This way only the token string can be read but they would need to be able to hash it again PASSWORD_DEFAULT in order to log in.

What about shared networks that can share IPs?

I ended up having an auth token per account, based on openssl random bytes. I put that in a cookie and check that upon login.

I make sure it matches the database value so if it’s modified in any way, it will not let them in, even if the cookie is set.

I’ll tell you, cookies are a female dog. I couldn’t get a cookie to clear for an hour. Turns out if you specify a directory for hte cookie, unsetting it must also have that directory explicitly set as well (I assume to match the right cookie?))

Anyway, the functionality is there.

Custom error messages / some additional JS functionality before login will be the only thing left to do :slight_smile: . Who knew login systems had so many parts to it…

The IP is instead of exposing a username, there is an random token added as well to identify shared ip’s, you can spoof it with a reverse proxy and it’s not reliable but it’s the same idea as using a id+username/password worst case they try to brute force it but they will have 20 mins max (timeout and remember me is invalid) or until the user performs an action.

Tell me about it, you only do it once. Thank god the major framworks take care of this stuff for us.

Yeah but it’s a shame that major frameworks already actually have it done. That means less coding in my free time. Definitely not what I want. Glad you are steering me away from it :wink: .

Or you could look at it as, you spend less time building basic functionality, and more time building your special business logic

2 Likes

So I should skip over solidifying basic PHP understanding and go straight to advanced stuff?

I’m not really seeing how that’ll help me if I skip warmup.

Either way, it’s too late as I am not going to abandon my work. As said countless times before…this is for educational purposes. I won’t learn anything if I get spoonfed.

I already said this was going to be available for code review. I will learn mistakes there.

Yes and No. Chances are you’ll have a hard time figuring out how to properly secure the basic functionality that frameworks will do out of the box.

You can always dig into the source of the framework to learn how they achieved that given task :wink:

Not saying you have to, but after you get through this, you may want to look at what it takes to implement your work using a framework :smile:

And there is value to frameworks, but you know that (you’ve likely used a few on the front-end side). Also, I simply was giving you a different way to view it. What you do with that info, not my business :wink:

Yes, I think about 3-4 people have recommended that so far. I plan on doing that after (to LEARN from mistakes), along with posting the class to be critiqued (also mentioned 3-4 times.)

That’s the point. Thinking critically like a programmer. I need practice with that.

Being spoonfed will do nothing for me. Absolutely nothing.

:thumbsup:

Wasn’t my point :wink: Just wanted to give you a different way of viewing it. It isn’t spending less time coding, it is utilizing your time to be spent towards what is important versus things that exist in just about any application :smile:

I get the whole, do it to learn bit though. Just wanted to give a different perspective as to why many choose to use a framework, they don’t want to focus on the little stuff.

For the record, I think it’s necessary for us to learn these things and just as valid to spend time learning how they work. Hence my own attempts, I’m not trying to put anyone off.

It’s just really productive in the real world to be able to say to clients, “yep we can do that, it will be secure and supported for years to come”. All I have to do is add a wildcard and update every so often, someone like Sensiolabs will then spend countless hours/audits to make it secure for me. Win!

Skipping warmup would be more like doing advanced features, like security, without a strong understanding of your environment.