9

I have spent a couple of months developing an application on a domain of mine. It's overall a simple concept. During development I hosted it myself on my own domain, but recently pushed it to our actual one. The problem is that sessions aren't created or kept between pages, and I can't for the life of me figure out why.

Apologize for the wall of code below, but I prefer it over a theoretical explanation.

Lets start with how I start my session at the top of every page:

function sec_session_start() {
    $session_name = 'login';
    $secure = false;
    $httponly = true;

    ini_set('session.use_only_cookies', 1);
    session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly); 
    session_name($session_name);
    session_start();
    session_regenerate_id();
}

And then how I check if the user is logged in. I added return x; instead of false for debugging. I append this to the redirect URL.

function login_check($mysqli) {
if(isset($_SESSION['id'], $_SESSION['login_string'], $_SESSION['type'])) {
    $id = $_SESSION['id'];
    $login_string = $_SESSION['login_string'];

    $user_browser = $_SERVER['HTTP_USER_AGENT'];

    if($_SESSION['type'] == 1 || $_SESSION['type'] == 2) // Admin user
    {

        if ($stmt = $mysqli->prepare("SELECT `password` FROM `users` WHERE `id` = ? LIMIT 1")) { 
            $stmt->bind_param('s', $id);
            $stmt->execute();
            $stmt->store_result();

            if($stmt->num_rows == 1) {
                $stmt->bind_result($password);
                $stmt->fetch();
                $login_check = hash('sha512', $password.$user_browser);
                if($login_check == $login_string) {
                    return true;
                } else {
                    return 1;
                }
            } else {
                return 2;
            }
        } else {
            return 3;
        }
    } else if($_SESSION['type'] == 3) { // Standard user
        if($stmt=$mysqli->prepare("SELECT `password` FROM `proj` WHERE `id` = ? LIMIT 1"))
        {
            $stmt->bind_param("s", $_SESSION['id']);
            $stmt->execute();
            $stmt->store_result();

            if($stmt->num_rows == 1)
            {
                $stmt->bind_result($db_key);
                $stmt->fetch();
                $login_check = hash('sha512', $db_key.$user_browser);
                if($login_check == $login_string) {
                    return true;
                } else {
                    return 4;   
                }
            }
        }
    } else {
        return 5;   
    }
} else {
     return 6;
}
}

I have two login pages, one for admins and one for users.

Admin:

<?php

ini_set('display_errors','On');
error_reporting(E_ALL);

include_once "../functions.php";
include_once "../db_connect.php"; 

sec_session_start();

if(login_check($mysqli) === true) {

header('Location: ../index.php');

}
else
{

// Login form

}

Users:

<?php

ini_set('display_errors','On');
error_reporting(E_ALL);

include_once "../functions.php";
include_once "../db_connect.php"; 

sec_session_start();

if(login_check($mysqli) === true) {

header('Location: ../index.php');

}
else
{

// Login form

}

Quite identical, apart from the file sources as the admin login.php is located in /admin. Despite this, the first one displays the login form correctly while the second redirects you to index.php (even when in incognito, a mystery to me), causing a redirect loop (as index.php sends it back for nog being logged in).

In addition to this, when I login with proper credentials, it does direct me to index.php only to redirect me back to login.php with error code 6.

Please do comment if you want more information or code examples, I feel lost in my own project right now.

Any help is appreciated, Thank you

UPDATE 17 dec:

After a few hours of debug, we've concluded that the issue is not with the code but with the server configuration. A simple example:

<?php

session_start();
echo session_id();

?>

If you open this file on the production server, and refresh the page, it displays a new session id with every request. I currently have no clue why. I have confirmed the session file is created as well as the cookie. It contains the proper information and can be accessed by SSH with server permissions. Truly some strange behavior.

Any clues?

Fredrik
  • 764
  • 1
  • 6
  • 22
  • make sure session is started only once per request, e.g if multiples files call `sec_session_start()` in the same request what will happen? – Nikos M. Dec 11 '15 at 11:58
  • different server configurations will handle sessions differently, e.g via cookies or files etc.. so the way the session is initialised makes a difference, these are just general comments to consider – Nikos M. Dec 11 '15 at 11:59
  • 1) Have you set a local `php.ini` on your different servers? settings such as `session.savepath` with have account specific addresses which will change on your testing and production servers. 2) Does your two servers have the same PHP version installed? 3) Run `phpinfo` and compare the output settings on the two servers to compare differences (such as session related addresses). Do any of these give you any curious answers? – Martin Dec 13 '15 at 14:06
  • Is your server address where the sessions are stored properly setup and enabled? As in has the correct permissions, and the folder exists etc? Theses are all currently simple trial and error debugs I'm running through – Martin Dec 13 '15 at 14:09
  • Thank you for your comments. 1) What I failed to mention in my question is that it's hosten on a subdomain, if that is of any interest. On the main domain a WordPress is hosted, which is working fine. Could this affect the session path in some way? 2) Yes, or very similar. 3) I will try this and look for differences! Regarding the session path, I first used `session_get_cookie_params()` and used this as save path and domain. The way it is now is simply a result of my first attempt at solving the issue. – Fredrik Dec 13 '15 at 14:44
  • @Fredrik subdomain should not be an issue as the PHP is all server side and does not care about the output address. Also `set_cookie_params` does not set the saving directory of the session, you need to review http://php.net/manual/en/function.session-save-path.php and let us know what path is set and if this path is accessible on your production server. – Martin Dec 13 '15 at 14:54
  • Is it possible your webhost is load balancing? In that case, unless your PHP is configured correctly, the sessions will exist independently on each server. – timclutton Dec 14 '15 at 13:15
  • what happens if you remove the ../ from `include_once "../functions.php"; include_once "../db_connect.php"; ` in the Users file? – Jonny C Dec 14 '15 at 17:52
  • @Fredrik See my answer. – Rajdeep Paul Dec 16 '15 at 18:27
  • Check this http://stackoverflow.com/questions/17242346/php-session-lost-after-redirect?rq=1 – wui Dec 19 '15 at 15:57
  • Thank you @koded, but I've already gone through those steps. Shame when a bounty still leaves you empty-handed. This is odd indeed – Fredrik Dec 20 '15 at 13:32

5 Answers5

5

I have gone through your code and I didn't find any thing unusal except only 1 thing.

You should never use == for string comparison === is OK.

$something = 0;
echo ('password123' == $something) ? 'true' : 'false';

Run the above code and you will find the reason of your session lost. In your function login_check you are using == for comparing two string

 if($login_check == $login_string)

replace it with:

 if($login_check === $login_string)

Else every thing is absolutly fine. If changing this little thing doesn't resolve your problem then just let me know.

Advice

You are connecting to DB before session start. I would recommend you to import your function then start your session and then connect to your database.

include_once "../functions.php";
sec_session_start();
include_once "../db_connect.php"; 
Vineet1982
  • 7,730
  • 4
  • 32
  • 67
  • I will try this as soon as I can, being tomorrow. If this is the solution, and it very well might be, how come it acts differently between two domains? – Fredrik Dec 14 '15 at 23:52
  • Can u post the code about how session are set. As here things are proper then you must setting session wrongly – Vineet1982 Dec 16 '15 at 04:04
  • What code do you mean specifically? I set the session in the first bracket I posted, and start it in the later brackets. – Fredrik Dec 16 '15 at 08:11
  • Can you try to use session_name() first before session_set_cookie_params() – Vineet1982 Dec 16 '15 at 09:58
  • Can you try to use session_name() first before session_set_cookie_params() it causes error sometimes. Kindly check it – Vineet1982 Dec 17 '15 at 12:27
  • I updated my question. It's unlikely to be an issue with the code, but rather the server itself. – Fredrik Dec 17 '15 at 15:54
  • can u provide details of http first and second request and other thing does your domain param is just a one level domain – Vineet1982 Dec 19 '15 at 20:39
  • Can you repeat that question? I don't quite understand what you're after I'm afraid :) – Fredrik Dec 20 '15 at 13:32
  • I need 2 things: 1) HTTP Request which are sent to your website; firebug in firefox and developer tools in Chrome would easily provide you that 2) Are you trying to use it on TLD i.e.. such as abc.local and setting cookies as .local as domain name; or abc.com setting domain name as .abc.com – Vineet1982 Dec 20 '15 at 18:06
4

Check if the session has already started and ensure it is only done once:

With PHP >= 5.4:

function sec_session_start() {
    if (session_status() == PHP_SESSION_NONE) {
        $session_name = 'login';
        $secure = false;
        $httponly = true;

        ini_set('session.use_only_cookies', 1);
        session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly); 
        session_name($session_name);
        session_start();
        session_regenerate_id();
    }
}

Prior to PHP 5.4:

function sec_session_start() {
    if (session_id() == '') {
        $session_name = 'login';
        $secure = false;
        $httponly = true;

        ini_set('session.use_only_cookies', 1);
        session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly); 
        session_name($session_name);
        session_start();
        session_regenerate_id();
    }
}

Additionally, why does the session have to be regenerated everytime?

Jan
  • 42,290
  • 8
  • 54
  • 79
  • I will try this, but both servers are up to date and shouldn't differ in this case. Regarding the regeneration: it currently does on every request as it's the easy way to keep it secure. That being said, it could be reduced to not every new request. I don't think the amount of requests is the problem here though, and it hasn't been in the previous cases I've used the code before. – Fredrik Dec 11 '15 at 13:53
0

I had experienced a similar issue few months back on my production server where sessions were getting expired(like in your case sessions are getting lost between pages) and users were getting logged out of my website abruptly. So after days of debugging everything came down to these three lines in php.ini file.

  • session.gc_divisor
  • session.gc_maxlifetime
  • session.gc_probability

From the manual,

  • session.gc_probability in conjunction with session.gc_divisor is used to manage probability that the gc (garbage collection) routine is started. Defaults to 1.

  • session.gc_divisor coupled with session.gc_probability defines the probability that the gc (garbage collection) process is started on every session initialization. The probability is calculated by using gc_probability/gc_divisor, e.g. 1/100 means there is a 1% chance that the GC process starts on each request. session.gc_divisor defaults to 100.

  • session.gc_maxlifetime specifies the number of seconds after which data will be seen as 'garbage' and potentially cleaned up. Garbage collection may occur during session start (depending on session.gc_probability and session.gc_divisor).

And these were my original session configurations in php.ini file which actually had created the entire nuisance in the first place,

session.gc_divisor 1000 1000
session.gc_maxlifetime 1440 1440
session.gc_probability 0 0

So this means the garbage collector will start with a probability of session.gc_probability divided by session.gc_divisor. And using the default values for that options (0 and 1000 respectively), the garbage collection probability = session.gc_probability/session.gc_divisor i.e 0/1000 = 0%, which I thought is incorrect, plus session.gc_maxlifetime seemed too low for me.

So, I decided to change session configurations in my php.ini file and included these values,

session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 28800

And since then it's working fine. So my suggestion to you is, check these three values in your php.ini file and if necessary, change them as per your need.

Rajdeep Paul
  • 16,887
  • 3
  • 18
  • 37
0

i had similar experience in the past, i don't exactly remember how i solve this problem, but from what i remember, it related to session cookies and variable order in PHP.

look for this line below in php.ini,

variables_order = "GPCS"

somehow, changing the order of those vars affected the PHP session.

if, by any change, like me, you can't change the setting on the server, you can also try duplicating the session cookie with regular cookie by doing this:

session_start();
set_cookie(session_name(), session_id());

and another note, different browser also have different behavior on session cookie, eg: safari. on safari, I have to duplicate the cookie regardless of variable order.

hope it helps

am05mhz
  • 2,727
  • 2
  • 23
  • 37
0

I also encountered that issue but it was ssl installation which changed the base file link with https and sending file was without ssl link. Also session length came to my mind. Other than that my be your hosting provider can explain this.

wui
  • 400
  • 3
  • 11