23

Ahoy StackOverflow,

I have come across this issue in my project: in short, from what I've gathered, is that the PHP script called via AJAX is not properly registering SESSION variables that were set at the top of the index.php page. At first, I assumed that it was due to session locking, so I went ahead and added session_write_close(), however, that didn't fix the issue.

This issue only occurs about 25% percent of the time after a new user session begins (ie: when a user logs in).

I went ahead and deleted 90% of the code to get the bug down to its bare minimum coding necessary to reproduce.

Bad result from Firebug via ajax.php

image

Expected result from Firebug via ajax.php

image2

Note: Both results show the return of index's print_r($_SESSION) as Array ( [userid] => 3724 [trialstatus] => 1 [trialtcompletions] => 0 [userlevel] => 5 ) which lets me know the issue isn't with the session being set on the index page.

Does anyone know a fix (perhaps not even code-wise, maybe even a server setting) that will properly allow the script called via AJAX to access the Session variable correctly?

Testing Scenario for Reproduction

  1. Delete all cookies for domain
  2. Load page (max 2 times). Issue never occurs after 2 reloads.
  3. If bad result isn't shown, repeat steps.

index.php

<?php

if (session_status() === PHP_SESSION_NONE) session_start();

if (!isset($_SESSION['userid']))
{
    $_SESSION['userid'] = 3724; //$login['AccountID'];
}

$_SESSION['trialstatus'] = "12";
$_SESSION['trialtcompletions'] = "12";
$_SESSION['userlevel'] = "12";
session_write_close();

print_r($_SESSION);
?>
<!DOCTYPE html><html><head><script src="./js/jquery.min.js"></script>
<script>
    function loadStage(step,input,callback){
        $.ajax({
            type: "POST",
            url: "./ajax.php",
            data: { step: step, input: input },
            dataType: "JSON",
            timeout: 5000,
            success: function(data){
                if(data !== false){
                    callback(data);
                }
            }
        });
    }

    $(document).ready(function(){
        startLoadingSequence();
    });

    function startLoadingSequence(skipped){
        loadStage(1,skipped,function(data){});
    }
</script>
</head></html>

ajax.php

<?php
if (session_status() === PHP_SESSION_NONE) session_start();

print_r($_SESSION);

if (!isset($_SESSION['userid']))
{
    die(json_encode(array(
        "error",
        "You must be logged in to view report data."
    )));
}
?>

Per request:

phpinfo

Read comments for extra information

TurdPile
  • 976
  • 1
  • 7
  • 21
  • 3
    Go into the Network tab of the console, and check whether `PHPSESSID` is being sent in the Request Cookies. – Barmar Jul 03 '15 at 01:39
  • 1
    I suspect it's a timing issue. You're sending the AJAX request before the browser has loaded the cookies. – Barmar Jul 03 '15 at 01:40
  • 1
    Yes, both good and bad results send the `PHPSESSID` cookie value. Since it does send the session ID, I doubt it's a timing issue (especially since the main script waits for a user interaction before ajax is even called). – TurdPile Jul 03 '15 at 01:43
  • If it's sending the cookie, it seems like it's sending the wrong session ID, and it's referring to an empty session. Does the PHPSESSID cookie match what was received by the `index.php` page? – Barmar Jul 03 '15 at 01:46
  • Where does the main script wait for user interaction? It's calling `startLoadingSequence` immediately in the `.ready()`. – Barmar Jul 03 '15 at 01:47
  • It was a side note. 90% of the original code isn't included here. I spent the majority of this afternoon deleting everything that didn't have anything to do with the bug to simplify the scope of the issue. – TurdPile Jul 03 '15 at 01:48
  • `$user->session_begin();` doesn't make sense, since `$user = "testUser";` – Barmar Jul 03 '15 at 01:49
  • Yep, both good and bad responses use the same Session ID that is assigned upon login. – TurdPile Jul 03 '15 at 01:51
  • `$user` is first used to start the PDO object, then $user is subsequently overwritten somewhere inside the PHPBB's included script. – TurdPile Jul 03 '15 at 01:52
  • Are you using multiple servers with a load balancer? – Barmar Jul 03 '15 at 01:52
  • 1
    Do yourself a favor and don't reuse variable names like that. – Barmar Jul 03 '15 at 01:53
  • From what I've been told, there're 3 servers: Game server (for the game), Apache server, and a database server. (Localhost in the DB host is actually an IP address to the proper database server) – TurdPile Jul 03 '15 at 01:54
  • I mean multiple webservers. A PHP session created on one server won't be accessible on another server. – Barmar Jul 03 '15 at 01:56
  • No, that's all on one server. – TurdPile Jul 03 '15 at 02:00
  • I'm running out of ideas. Are there any errors in the PHP error log on the server? – Barmar Jul 03 '15 at 02:01
  • I'd have to get back to you on that one. I only have limited access to the server. – TurdPile Jul 03 '15 at 02:02
  • Just to make sure session id is the same in both scripts, try to add `echo session_id();` to both of them (after `session_start()` line), and check if they match when you get bad result. – pisamce Jul 03 '15 at 02:09
  • @pisamce Yep, they are identical. – TurdPile Jul 03 '15 at 17:48
  • @Barmar no errors in the logs – TurdPile Jul 04 '15 at 16:04
  • Is `session.auto_start` enabled? Or why are you calling `session_start()` only when no session exists? Also do you have all logging enabled (`error_reporting` set to `E_ALL`)? Otherwise you won't see NOTICEs... – Marki555 Jul 05 '15 at 21:03
  • Have you tried another (vanilla) browser? Some installed browser plugins/extensions may give very unexpected results when debugging. – vonUbisch Jul 05 '15 at 21:15
  • @Marki555 checking errors with error_reporting was the first step I did upon realizing the error; nothing shows. session.auto_start is disabled. – TurdPile Jul 06 '15 at 01:13
  • I have removed both the phpBB integration and the database queries and made the example simpler. Bug still occurs without any of that stuff. This makes me think its some server setting. – TurdPile Jul 06 '15 at 01:33
  • 1) What about `$_COOKIE["PHPSESSID"]` when the error raised? 2) Have you tried to reproduce the error in a different test environment? – stdob-- Jul 06 '15 at 01:59
  • @stdob-- 1) the PHPSESSID is being passed properly as stated in the above comments. 2) Works fine on my local server, however, I am also using a different version than the production server – TurdPile Jul 06 '15 at 03:57
  • Speaking of local server, I'm going to do a compare/contrast of `phpinfo` readouts when I get home from work in the afternoon. More and more it seems to be a server issue. – TurdPile Jul 06 '15 at 04:04
  • `if (session_status() === PHP_SESSION_NONE) ? session_start();` check now – Abdulla Nilam Jul 06 '15 at 04:20
  • If `session.auto_start` is disabled, you must call `session_start()` also for existing sessions: https://secure.php.net/manual/en/function.session-start.php – Marki555 Jul 06 '15 at 07:06
  • Try the following: `session_status() === PHP_SESSION_NONE || session_status() === PHP_SESSION_ACTIVE` (i.e. check for both because by the time you get to the AJAX script the session is enabled and exists. PHP_SESSION_NONE checks if session is enabled but does not exist). – Ricardo Velhote Jul 06 '15 at 07:55
  • @RicardoVelhote Nope, didn't work. – TurdPile Jul 06 '15 at 10:47
  • @TurdPile I think this is down a timing issue. Can you try to use `session_set_cookie_params` and set a specific lifetime instead of 0 (which means: until the browser is closed) [Documentation](http://php.net/manual/en/function.session-set-cookie-params.php) – Ricardo Velhote Jul 06 '15 at 13:36
  • @RicardoVelhote Tried 600, error still occurs. – TurdPile Jul 06 '15 at 19:46
  • @TurdPile Can you please post full header information, full cookie information (path, expire date, domain etc) from a failed and successful request? – Ricardo Velhote Jul 06 '15 at 20:18
  • Are you sure your version of PHP in production is >= 5.4.0 (see : http://stackoverflow.com/questions/6249707/check-if-php-session-has-already-started). And what is you PHP version actually ? – β.εηοιτ.βε Jul 06 '15 at 21:04
  • @b.enoit.be [Snapshot](https://i.gyazo.com/35fdd61a551349c923ea7d78bd2d9160.png) – TurdPile Jul 06 '15 at 23:45
  • @RicardoVelhote Not sure what you are asking for. the `PHPSESSID` cookie isn't the issue, as the cookie (as stated near the beginning of this comment block) is sent to the ajax file fine. – TurdPile Jul 06 '15 at 23:59
  • @TurdPile I'm asking for the HTTP Headers sent and received. – Ricardo Velhote Jul 07 '15 at 07:06
  • @RicardoVelhote [Working](https://i.gyazo.com/c0eea4f92b5446e5e0c29a00f4f1e506.png) - [Not Working](https://i.gyazo.com/fa45476ebde255306a8edabb0adc1afa.png). No differences except the `Vary: Accept-Encoding` – TurdPile Jul 07 '15 at 12:23
  • @TurdPile I accessed the URL in the screenshot (the reproduceBug.php script) and I've tried it in Firefox and Opera and I could not replicate the issue. I've refreshed the page 20 times each. I can only conclude that your browser is doing something funky. From my end it's working fine. My suggestion is creating a new Firefox profile (with no extensions) and/or try multiple browsers with a clean slate. – Ricardo Velhote Jul 07 '15 at 14:01
  • @RicardoVelhote You must clear cookies to get a new session. The issue will never occur after 2 reloads, see `Testing Scenario for Reproduction`. And I know it isn't just me because it happens for everyone that I've had check, including the person I work for, and an associate; as well as occurring in both Chrome and IE for me personally as well. This problem really has me stumped. – TurdPile Jul 07 '15 at 17:01
  • @TurdPile I managed to replicate it. I was using a private browser session and that influences the result somehow (although it still happens eventually). The last thing I can think of is Cloudflare. Can you try to do this directly on the server bypassing Cloudflare? – Ricardo Velhote Jul 07 '15 at 19:34
  • @RicardoVelhote I don't think so. I only have access to the fileserver. What would Cloudflare cause? – TurdPile Jul 07 '15 at 22:07
  • @RicardoVelhote Apparently the server is running 2 load balancers. Just got that information. – TurdPile Jul 08 '15 at 03:22
  • @TurdPile That's the most logical explanation then. Since you mentioned there were no load balancers in the beginning I ignored that part :) Try it on a single server if you can - that's the confirmation. – Ricardo Velhote Jul 08 '15 at 07:28

3 Answers3

11

Two things can cause this issue.

  1. You don't have enough space in session save path (df -h) or you server don't have permission to save it.
  2. Your server is behind a load balancer, and you must save sessions in a persistent backend like memcache or redis.
Jordi Martín
  • 509
  • 4
  • 12
  • For my understanding, how does a load balancer effect the AJAX that doesn't effect the initial page itself? – TurdPile Jul 08 '15 at 03:26
  • @TurdPile It probably does affect but you are setting the session data (in index.php) and then doing the `print_r`. Try putting the `print_r` after session start and you might see it happening (the first time it will be empty of course). – Ricardo Velhote Jul 08 '15 at 07:23
  • @TurdPile Also it is possible that, in the main application, they are using a persistent backend (like Jordi mentioned) so you should not be using `$_SESSION` directly. – Ricardo Velhote Jul 08 '15 at 07:46
  • @RicardoVelhote You mean `session_start` again after the `session_write_close` ? – TurdPile Jul 08 '15 at 14:51
  • @TurdPile Can you provide us a server response information? I look for a custom header like "X-" or any thing similiar. – Jordi Martín Jul 08 '15 at 15:03
  • @RicardoVelhote Prints nothing, and AJAX gets it. Second load, AJAX errors, but print_r on first page is populated. – TurdPile Jul 09 '15 at 02:50
  • Sometimes my problem with session depends on url, url: "./ajax.php", have you try BOTH : url: "http:// www.yourdomain.it/ajax.php" AND THEN "http:// yourdomain.it/ajax.php" ? – Luca Olivieri Jul 10 '15 at 14:49
8

If you're running a load balancer then you have to make sure your servers are hitting a common point for data. By default PHP stores sessions in the local file system. That becomes a problem if your load balancer sends you from server A to server B, where that file doesn't exist. You could set up a network share and make sure all web servers use that share. So you could create an NFS share and then add session_save_path or set it within php.ini

session_save_path('/your/nfs/share/here');

Another option is to write your own session handler that puts sessions into your database. You could then use something like memcached to store the sessions in a way that you won't hammer your DB every time you read your session data.

Machavity
  • 30,841
  • 27
  • 92
  • 100
  • 1
    NFS doesn't support needed locking, so should NOT be used for PHP sessions storage - https://stackoverflow.com/questions/12552955/why-not-storage-php-session-on-a-nfs-volume – Marki555 Jul 10 '15 at 15:19
  • @Marki555 Interesting link. I'd like to see how well it runs under something faster than traditional magnetic drives (i.e. AWS Provisioned IOPS). That question is almost 3 years old. And I do prefer Memcached myself but it's overkill for low traffic. – Machavity Jul 10 '15 at 18:08
1

The screenshot I have attached is same for almost all 25-30 runs I have run. [1]: https://i.stack.imgur.com/MgLpS.jpg Check your session data size maybe it's exceeding the cache size. or some other session data is being stored exhausting your cache memory. Increase your cache size maybe it will help for your scenario.

Varshaan
  • 555
  • 9
  • 22