EleventyOne
EleventyOne

Reputation: 7512

PHP: session_start() called 'simultaneously' in multiple tabs creates multiple sessions

I've stumbled across an interesting problem. A site I'm working on has, at the moment, three pages: login.php and welcome.php and ajax.php. They all call session_start() at the top of their code. Login.php checks the $_SESSION[] variables to see if someone is logged in; if not, it gets their name/pwd, contacts ajax.php to check the name/pwd, and logs them in by setting the appropriate $_SESSION[] vars. Welcome.php looks for the appropriate $_SESSION[] vars and displays a welcome message to the user - if they're not set, it asks the user to please login. Typical behaviour, and if used in a typical fashion it works perfectly.

However, if you bookmark login.php and welcome.php as a set of tabs (e.g., in Firefox) and then open them both at the same time, something odd happens. Perhaps because session_start() is called twice at the exact same time (checked by using error_log()), two separate sessions are created (checked with session_id()). Whichever session_start() happens to be called "last" (although same timestamp) is the session that remains. This causes trouble in the following scenario: login.php's session_start() is called before welcome.php's session_start() is called.

In this situation, the session created in login.php continues to exist in that page as long as it's not refreshed. However, when it contacts ajax.php to check name/pwd details, and ajax.php calls session_start(), it retrieves the session created by welcome.php, which has nothing whatsoever in the $_SESSION[] variable, making the whole thing fail. So if login.php calls session_start() first, I have to figure out a way to prevent welcome.php's session_start() from creating a new one. Note: The calling order is not consistent, and everything obviously works fine as long as login.php's session_start() is called last (which I can't control).

Ideally, there would be some way for the second file to notice that the first file is in the process of creating the session, so it shouldn't create it's own (but only for welcome.php).

This MUST be a problem that others have dealt with, but I have been completely unable to find any mention of it on these here interwebs. Any help would be greatly appreciated.

EDIT1: Here are the files needed to reproduce the problem:

LOGIN.PHP

<?php
session_start();
error_log("login.php, session id: ".session_id());
$_SESSION['user'] = "EleventyOne"
?>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>multiple session bug - login</title>

<script src='jquery-1.9.1.js'></script>
<script>

    $(document).ready(function() {
        $.ajax({
                url : 'ajax.php',
                data : { 'func' : 'check_login' },
                dataType : 'json',
                type : 'GET',
                timeout : 10000
            })
        .done(function(data,textStatus,jqXHR){
            alert("Done: "+data.status);
        })
        .fail(function(jqXHR,textStatus,errorThrown) {                    
            alert("Failed: " + textStatus + "(" + errorThrown + ")");
        });
    }); // end ready

</script>
</head>

<body>
</body>
</html>

WELCOME.PHP:

<?php
session_start();
error_log("welcome.php, session id: ".session_id());
$message = "";

if ( isset($_SESSION['user']) ) {
    $message = "Hello, ".$_SESSION['user'];
}
else {
    $message = "Please login!";
}
?>

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>multiple session bug - welcome</title>
</head>

<body>
<div>
<?php echo $message; ?>
</div>
</body>
</html>

AJAX.PHP:

<?php
session_start();
error_log("ajax.php, session id: ".session_id());

// ignoring $_GET[] here, as superfluous to the point...

if ( isset($_SESSION['user']) ) {

    // check the database for that user...
    // ...

    // return status
    $ret['status'] = "ok";
    echo json_encode($ret);
    exit;
}
else {
    // return failed status
    $ret['status'] = "broken session";
    echo json_encode($ret);
    exit;
}
?>

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>multiple session bug - ajax</title>

<script src='_js/jquery-1.9.1.js'></script>
<script>

    $(document).ready(function() {
    }); // end ready

</script>
</head>

<body>
</body>
</html>

If you load login.php, open a new tab and load welcome.php, here's the error_log file you get (everything is fine):

[27-Jun-2013 18:39:40 UTC] login.php, session id: skofpr8g0tg81aqohnkahv3vk5
[27-Jun-2013 18:39:40 UTC] ajax.php, session id: skofpr8g0tg81aqohnkahv3vk5
[27-Jun-2013 18:39:44 UTC] welcome.php, session id: skofpr8g0tg81aqohnkahv3vk5

If you bookmark login.php and welcome.php as a set of tabs, close your browser, re-open it, and open both tabs at the same time, you will get one of two error_log files, depending on which session_start is called first.

This one works, as login.php's session_start() persists for ajax.php. So ajax.php reports status of "ok":

[27-Jun-2013 18:40:39 UTC] welcome.php, session id: 6q2q96lhhoaqqhj214gs3gos36
[27-Jun-2013 18:40:39 UTC] login.php, session id: j8eaa5mtfsla9q3q80qt03kvt7
[27-Jun-2013 18:40:39 UTC] ajax.php, session id: j8eaa5mtfsla9q3q80qt03kvt7

This one doesn't work, as welcome.php's session_start() persists for ajax.php, so ajax.php reports "broken session":

[27-Jun-2013 18:40:18 UTC] login.php, session id: s4b7jo41jpg1ubbe8at7c5qr35
[27-Jun-2013 18:40:18 UTC] welcome.php, session id: freu86sn3edc3fuoc2pn875o90
[27-Jun-2013 18:40:18 UTC] ajax.php, session id: freu86sn3edc3fuoc2pn875o90

Upvotes: 3

Views: 4221

Answers (2)

EleventyOne
EleventyOne

Reputation: 7512

The best solution I could come up with was to move the session "creation" (i.e., setting session variables) into the ajax.php file, executed only after a user has successfully sent their uname/pwd and so they are about to be redirected to a new page anyway (i.e., welcome.php). This means that login.php cannot be guaranteed to have access to any session variables set by ajax.php whatsoever, so it's just a dumb page that relies solely on its ajax calls to know what's going on. As it turns out, this isn't such a hassle after all.

Upvotes: 1

Derek
Derek

Reputation: 1196

I doubt one process if overwriting the other. Only one process can have the same session file open at any time. The 2nd (and subsequent) will hang waiting for the session file to become available.

From my experience, $_SESSION isn't really as magical as it seems, it just uses browser cookies or something to make sure reads from and writes to the same file on disk every time. I got around this problem previously by only using session_start() when I needed. In other words, instead of opening the session at the top of every page, I did a session_start immediately before writing or retrieving information from $_SESSION and did a session_write_close as soon as I was done with it.

Upvotes: 0

Related Questions