Jump to content

Going from SESSIONS only to REMEMBER ME


tsangaris
Go to solution Solved by mac_gyver,

Recommended Posts

Hi,

 

I have created a webpage that so far was working using SESSIONS.

 

As soon as the user successfully logs in, i save some variables inside a SESSION array and pass them to the mainpage.php script (or any other script that needs these variables).

 

Now i want to implement "REMEMBER ME" feature. I know that i need to store the variables i want into a COOKIE and then access the cookie to get the variables i want.

 

How do i restructure my code now?

 

In case of not selecting REMEMBER ME:

I check to see if a SESSION is set and i use only the SESSION variables?

 

 

In case of selecting REMEMBER ME:

I check to see if a cookie is set and then retrieve the variables from COOKIE array?

 

If thats the case i will need to check every script to check this? Is there an easier way to configure it?

 

Regards,

 

Chris

 

Link to comment
Share on other sites

I would think that if someone logs in while selecting the remember box checkbox, that is when you save the cookie. You always save any specific login info that your appl will need later on that session, but you won't always save a cookie (probably holding the userid only).

 

If the user logs in without checking the remember me box, then you simply don't save a cookie.

 

I imagine the cookie would have some application-specific name to it.

Link to comment
Share on other sites

I would think that if someone logs in while selecting the remember box checkbox, that is when you save the cookie. You always save any specific login info that your appl will need later on that session, but you won't always save a cookie (probably holding the userid only).

 

If the user logs in without checking the remember me box, then you simply don't save a cookie.

 

I imagine the cookie would have some application-specific name to it.

Yes, the cookie is created after the user's credentials are correct and if the REMEMBER ME checkbox is checked.

 

I will try to explain the way my website is structured so you can help me:

 

a) user puts his credentials to login

b) credentials correct: create a SESSION to store user_email, user_hashed_pwd and login_status (boolean TRUE)

c) redirecting to main page

d) using login_status it checks if the user is logged in then proceeds

e) using user_email, hashed_pwd it performs some calculations (like getting the name of the logged in user, etc)

f) since i keep these variables inside a SESSION i can use them inside other scripts to perform the same calculations (or others)

 

As you understand all my scripts rely on the SESSION variables.

 

What is the procedure i have to follow when the user clicks REMEMBER ME? Do i bypass SESSIONS and use COOKIE?

How do i minimize current code alteration? Do i need to go to each script and call the variables with COOKIE as well (as i do when SESSION is used?)?

 

Can i mix COOKIE with SESSION? For example use COOKIE to get the userID after login procedure, and after using userID to get the email address of the user, pass the email to a SESSION?

This SESSION will be used to other scripts that need the email to work..

 

BTW i am confused, so maybe i get it all wrong! Please correct me where i am wrong! :)

Link to comment
Share on other sites

when a user logs in, your session based login should store the user id from the row in your user database table. that's all you need/should have in the session to IDENTIFY who the user is. you should then use that session user id value to query for things like the permissions the user has.

 

to add a remember me feature, you would generate a unique and random token (see openssl_random_pseudo_bytes()), store the token in a cookie and store it and the user id in a database table. set the database table column to be a unique key to enforce the uniqueness of the token (regenerate the token should a collision occur.)

 

as part of your login check code, if the rememberme cookie is present and the current user is not logged in, use the token value from the cookie to find the corresponding user id value. store that user id value in the normal session variable. the rest of your code remains the same, using the user id from the session variable.

 

when the person logs out, mark the matching row in the database table as being logged out (a separate column in the table.) this will prevent that token value from being usable by that person or any one who gets a hold of it.

Edited by mac_gyver
Link to comment
Share on other sites

Along with mac_gyver's great post let me re-enforce something he alluded to: NEVER STORE A PASSWORD IN ANY WAY, SHAPE OR FORM. You use a password to interrogate your authentication table and once used you discard it. Create a token or simply use the username (key) to keep track of the user's status. One doesn't need the password. If you need anything else from the login process store some token or the actual 'something' but NEVER SAVE THE PASSWORD! Storing a password value only leaves you open to some malicious activity where you inadvertently leave yourself vulnerable or where some other vulnerability in the system allows a hacker to see what you have saved.

Link to comment
Share on other sites

Thank you both for your answers! I really appreciate the feedback!

 

So, long story short, whatever i use (SESSION or COOKIE[for remember me]) i should only store the userID, the login_status[only for SESSION] and a unique token?

 

The login_status will be used to check whether the user is logged in in case i use SESSIONS.

 

As for the COOKIE, i will only need to store the userID and a random generated token that will be unique.

 

After i use the userID and the unique token to check if this is a real user, then i use the userID to extract all user related information and assign them to SESSIONS and move on as i am now?

 

Thanks again!

Link to comment
Share on other sites

That sounds mostly correct.

 

Think of it this way: you're going to have two separate authentication methods. One method is typing username/password, the other method is supplying a user ID and token. Both methods will log in a user, and after that, the application should function identically no matter which method is chosen. That means that anything you're storing in the session when they're logging in with a username/password, you should also add to the session if they're logging in via a cookie.

 

"Remember me" is not an alternative to sessions. You should still use sessions, to prevent having to repeat the authentication on every single page load.

Link to comment
Share on other sites

I think i get it now.

 

If i use Remember me feature i will create a token, insert the token+userID+logged_in_status inside a table, and at the same time create a COOKIE that will hold the value of that token. At every visit i will access this COOKIE, check the token against the table and login the appropriate user.

 

If no Remember me feature, then i will store the set the login_status = TRUE, and store the loggin_status and userID inside the SESSION (is it OK to store userID inside the SESSION?). At every visit i will check the login_status and if TRUE then i will proceed with the rest of the page.

 

Both methods will then continue by extracting the user data according to the userID. All variables needed to other scripts will be stored inside SESSION variables, and accessed using session_start() at every script requested.

 

Right? :)

Link to comment
Share on other sites

At every visit i will access this COOKIE, check the token against the table and login the appropriate user.

You could do it that way, but that's not the way I would go. I would only check for the cookie if they don't already have an active session. So it would go like, first visit: check cookie, get token, match to database, create session. Second visit: session already exists from previous request, so carry on as usual. If the session expires or they have no session, check for the cookie - if no cookie, redirect to login where they re-enter username/password.

Link to comment
Share on other sites

  • Solution

example code that implements the suggested logic -
 

to add a remember me feature, you would generate a unique and random token (see openssl_random_pseudo_bytes()), store the token in a cookie and store it and the user id in a database table. set the database table column to be a unique key to enforce the uniqueness of the token (regenerate the token should a collision occur.)

// at the point in your code where you have determined that the login was successful -
$_SESSION['user_id'] = $row['id']; // remember who the logged in user is

// check if rememberme is set
if(isset($_POST['rememberme'])){
    $max_attempts = 5;
    // the token column is defined as a unique index in the table
    $query = "INSERT IGNORE INTO rememberme (user_id,token,created,active) VALUE ({$row['id']},?,now(),1)";
    $stmt = $pdo->prepare($query);
    $stmt->bindParam(1,$token);
    for($x = 0; $x < $max_attempts; $x++){
        $token = bin2hex(openssl_random_pseudo_bytes(40)); // 40 bytes/80 hex chars
        $stmt->execute();
        if($stmt->rowCount() > 0){
            // row was inserted
            setcookie('rememberme',$token,time()+60*60*24*365,'/');
            break;
        } else {
            // row not inserted - this should be very rare
            trigger_error("duplicate rememberme token occurred");
        }
    }
    if($x == $max_attempts){
        // user error message
        $errors[] = "the remember me function did not work. the site admin has been notified.";
        // system error message
        trigger_error("max attempts used when generating rememberme token"); // this should be even rarer or there's an error in the code/data
    }
}

as part of your login check code, if the rememberme cookie is present and the current user is not logged in, use the token value from the cookie to find the corresponding user id value. store that user id value in the normal session variable. the rest of your code remains the same, using the user id from the session variable.

function logged_in(){
    return isset($_SESSION['user_id']);
}


// rememberme check
if(!logged_in() && isset($_COOKIE['rememberme'])){
    $query = "SELECT user_id FROM rememberme WHERE token = ? AND active = 1";
    $stmt = $pdo->prepare($query);
    $stmt->bindvalue(1,$_COOKIE['rememberme']);
    $stmt->execute();
    if($user_id = $stmt->fetchColumn()){
        $_SESSION['user_id'] = $user_id;
    }
}

// existing/typical login check
if(!logged_in()){
    // do what you normally do here when not logged in
    header('location: login.php');
    die;
}

// at this point, the user IS logged in, either because he had a valid rememberme cookie or he already had a current session that identified him, use the session data any way you need
var_dump($_SESSION);

 
you should not store things like user permissions or the usrename (if you want to allow the user to change his username) in the session data as this will mean that you cannot alter these on the fly and have them take immediate affect. you should query on each page request for the user values that can change.

Edited by mac_gyver
Link to comment
Share on other sites

Dear mac_gyver,

 

I am trying to implement what you said and i have to say you have guided me to the right direction!

 

Just a question:

 

Every time userA logs in using REMEMBER ME, the system will create a new token, and insert this token along with userID (and other details) inside the remember_me table in a new row?

 

Lets say that userA logs from Chrome, Safari and Firefox. This will create 3 entries inside remember_me table right?

 

id | userID |  token   |   active   |

1       40        abc1         1             --> From Chrome

2       40        abc2         1             --> From Safari

3       40        abc3         1             --> From Firefox

 

 

At the same time if the userA logs out from Chrome, it will only turn active = 0 to the row created because of logging in from Chrome? So during the logout process i need to check if the cookie is set and then go and find the user with userID == 40 and token == abc1 and only turn active to 0 in that row?

 

Also in the logout script i will need to unset the rememberme cookie in addition to changing the active flag from 1 to 0? Is this the correct way to unset a cookie?

 

setcookie("rememberme", "", time()-10, "/");

 

id | userID |  token   |   active   |

1       40        abc1         0             --> From Chrome

2       40        abc2         1             --> From Safari

3       40        abc3         1             --> From Firefox

 

One last question: As i understand the remember_me table will soon be full with rows that will not longer used for something (i mean the rows where the user logged out when previously used the REMEMBER ME feature). Do i need to remove them from time to time?

 

Thanks (once again) a lot!!

 

Christos 

Link to comment
Share on other sites

you need to keep the tokens in the table, at least for some amount of time, to insure uniqueness.

 

This will create 3 entries inside remember_me table right?

 

 

yes, that is correct. you would want a user to be able to login and be remembered separately and independently on different devices/browsers.

 

So during the logout process i need to check if the cookie is set and then go and find the user with userID == 40 and token == abc1 and only turn active to 0 in that row?

 

 

yes, you need to match both the correct userid and token value when setting the active flag to 0.

 

will need to unset the rememberme cookie in addition to changing the active flag from 1 to 0?

 

 

it's not necessary to unset/delete the cookie. i consider it a waste of time, since you actually aren't removing the cookie in the client. you are setting the time in it to the past (hopefully at least one day in the past so that computers in all timezones, relative to the server's timezone will end up with a time in the past) so that the client won't send it to the server any longer. the cookie and the token value in it will actually still be stored on the client.
 

 

Do i need to remove them from time to time?

 

 

yes. you probably should also have a 'last accessed' time column in that table that you UPDATE each time the rememberme row is accessed. this will let you know how long in the past the row was used. you can then periodically (via a cron job/scheduled task or even via a database trigger on that table) remove rows that haven't been accessed recently.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.