4

This is NOT a duplicate of this Q&A because I have checked each answer to that Q&A but none of them are improving the situation here.

Same also applies for this Q&A and this Q&A.

Yes, the issue is a "classic" one but I can't see how to diagnose this: The session is saved to the file and the session file saves all the relevant data, but on every page load the PHP system doesn't find the existing session file and instead generates a new session.

I can't for the life of me see why this is occuring.

UPDATE:

I have found this morning (1st August) that a recent update to Firefox to 115 seems to have this side effect whereby Firefox browser can't save/load/use/reach the cookie that contains the session id or otherwise the session id is not being saved to the cookie. Please see "Update" details at the bottom of this question.

Opera, Chrome and Edge browsers work fine and carry sessions correctly.


Server

  • The server runs on Plesk Obsidian 18.0.54 and is currently running PHP 7.4.33
  • The server currently runs PHP as an "FPM application served by Apache" (see note at very bottom).
  • The server previously ran everything in the "original" state absolutely fine and without issue.
  • Session are saved using the file system.
  • There is no issue with disk space usage.

Something has changed and now PHP Session id's are not being transferred between pages. Each page initiates a new session.

Local PHP.ini

The local PHP ini file has the following settings:

session.save_path="/path/to/domain.uk/sessionFolder"
session.name="randomString89"
session.use_only_cookies=1
session.cookie_httponly=1
session.cookie_secure=1
session.cookie_samesite="Strict"
session.cookie_domain=".domain.uk"
session.cookie_path=/
session.sid_length=128

I have tweaked some of these settings (exploring cookie_samesite="Lax") and also removing quotes around strings and these have made no difference to the issue.

Original Structure

The first page in question (A.php) has a few included files:

A.php

-- Header.php
-- Preloader.php
-- Core page script

header.php
A standard header that is universal to all pages on the website and which sets header info such as HTTP headers as well as ensuring the autoloader is correctly set and that a PHP session is active.

error_reporting(E_ALL &~E_NOTICE);

if (session_status() === PHP_SESSION_NONE){
    session_set_cookie_params( 4000, '/', '.domain.uk' , true, true );
    session_start();
}

header("Cache-Control: no-cache, must-revalidate"); //HTTP 1.1
header('Content-Type: text/html; charset=utf-8');
header("Content-Language: en");
header("Content-Security-Policy: upgrade-insecure-requests;");
header("Referrer-Policy: origin-when-cross-origin"); //referrer for Chrome
header("Referrer-Policy: strict-origin-when-cross-origin");
...

use \namespace\reference\donehere;
// custom autoloader (100% works)
require str_ireplace("/public_html","/classes",$_SERVER['DOCUMENT_ROOT'])."/autoloader.php";

I have explored all sorts of variations with this such as setting the session cookie lifetime to 0, confirming the domain is fine, even disabling the session_set_cookie_params() line entirely. No changes to the behaviour.

preloader.php
After the header, the first page loads a "preload" include script which sets a few session variables for use for the entire client visit. This works perfectly and only interacts with sessions here:

if( empty($_SESSION['reference']) || (time() - (int)$_SESSION['referenceTime']) > 15 ) {
    $_SESSION['reference']         = "generated value here";
    $_SESSION['referenceTime'] = time();
    error_log("\nNEW checkDrop set in ".__FILE__." as: ".print_r($_SESSION,true));
}
elseif(strlen($_SESSION['reference'])> 0) {
    error_log("\ncheckDrop already exists: ".print_r($_SESSION,true));
}

These are brought together within the first page, A.php :

A.php

<?php
use \namespace\reference\donehere;
require $_SERVER['DOCUMENT_ROOT']."/path/to/header.php";
include $_SERVER['DOCUMENT_ROOT']."/path/to/preloader.php";

if (isset($_GET['msg'])) {
    $_SESSION['message'] = trim(($_SESSION['message']??'').' '.urldecode($_GET['msg']));
}

?>
<!DOCTYPE html>
<html class="no-js" lang="en-GB"> 
<head>
    <meta charset="utf-8">
    <title>title</title>
    <link rel="canonical" href="https://www.domain.uk/A.php" >
    <?php
    include $_SERVER['DOCUMENT_ROOT']."/path/to/html_only_metaheader.php";
    ?>

</head>
<body>

<main>
    <section>
        <form name="form1" method="post" action="/B.php" 
              enctype="multipart/form-data" accept-charset="UTF-8">

there then follows a form which contains a hidden field holding some session data:

            <input type='hidden' name='xxxx' value='<?php
            // value comes from the preloader include above.
            print $_SESSION['reference']??'';
            ?>'>

At the bottom of the page is session debug output which always shows the correct data and the same as the HTML form above:

</body>
</html>
<?php
error_log("base of start: ".print_r($_SESSION,true));
error_log("Session ID: ". print_r(session_id()??'none',true));

B.php

  • header.php
  • Session and POST data processing in the script.

And that's it, the original page was not in the same folder but even in the same folder there is a complete failure for the session to realise there's an existing session associated with this

<?php
use \namespace\reference\donehere;

require $_SERVER['DOCUMENT_ROOT']."/path/to/header.php";
error_log("Session ID 3: ". print_r(session_id()??'none',true));
error_log("name of session 3: ".print_r(session_name(),true));

Things I've done to explore this issue:

  • Session name ("randomString89") is universal throughout all pages/include.
  • (Prolific) error log reporting shows SESSION data is being written to file.
  • Have tried disabling/skipping session_set_cookie_params (it's all set in the ini anyway tbh)
  • Trying to set samesite.cookie to Lax
  • And ensuring domain is open / or open to www. or other variations .domain.uk
  • Session status is confirmed as "active" (2) on all pages after the header.php session_start();
  • There is no whitespace (that I can see) that would interfer with session triggering.
  • PHPinfo shows nothing unexpected and everything appears correct under the "local" column.
  • All found StackOverGnome solutions don't resovlve this or don't apply (outlined at the top of this question)
  • I have spent many hours today methodically working through possibilities here and not found anything. What have I missed?

How can I diagnose this problem?

  • Server note: The server currently runs PHP as an "FPM application served by Apache". There are options here to change this to "FPM application served by nginx" or "fastCGI", I have never changed this on this server and not sure what this could entail?

Updates

I have found some unexpected updates: The issue appears to be specific to Firefox 115. I have tried Opera Browser (v100), and Chrome (v115) and even MS Edge (v109) and they all work fine, but Firefox persists in not recognising the session id . Strangely, in other websites (both mine and 3rd party) there is not log in issues with Firefox so kind of curious why this seems browser specific.

To answer queries from the comments:

  • The page saves the session data fine, and checking the raw session file in the filesystem confirms this.
  • Every page loads "header.php".
  • I have tried both using .domain.uk and www.domain.uk and domain.uk and these don't make a difference.
  • Disabling Firefox's "Enhanced Tracking Protection" does not resolve this issue.

To underline, the issue doesn't appear to be the session mechanism itself, but somehow the browser (Firefox 115) doesn't seem to be saving the [correct?] session ID to the cookie or similar inconsistency.

Martin
  • 22,212
  • 11
  • 70
  • 132
  • @hakre I listed the Q&A that I've explored extensively at the top of this Q. I think this Question is far too long already, and didn't want to regurgitate what the other Q&As contain. – Martin Jul 31 '23 at 22:06
  • [`session_start`](https://www.php.net/manual/en/function.session-start.php) states *"Start new or resume existing session"*. But it's only called if there is no session, shouldn't it be always called? – A.L Jul 31 '23 at 22:10
  • @A.L It is always called when the page loads and the header.php doesn't find an existing open session. Because sessions are not maintained between pages the header.php session trigger should always fire. Thus always running `session_start();` . There is no indication the session isn't starting, but it's that it's never **resuming** a current session for this client. – Martin Jul 31 '23 at 22:12
  • Yes the question is too long and the introduction is so little saying while creating the imporession (to me), that you have something very specific. But then where are the specs. You say it's a classic (whatever that means) and then you continue talking about how apache wires with fpm. As if any of the combinations wouldn't pass HTTP messages properly any longer .... I mean that's cookies you're talking about, right? – hakre Jul 31 '23 at 22:13
  • @hakre apologies, I've been digging on this for most the day so may loose sight of how it comes across to a fresh mind. in short; the issue is that the PHP system for whatever reason isn't resuming existing sessions. I've established a current session is set up by the usual methods, but each new page load can't find any existing ID and instead sets a new one. I can't see why this is so. – Martin Jul 31 '23 at 22:16
  • @hakre I have edited and summarised the above at the top of the question. I have not made recent changes to this website so I feel the issue would be something central on the server. If you feel the issue can't be the PHP handler mechanism (CGI / FPM etc) then I'll happily take that out of the question! – Martin Jul 31 '23 at 22:21
  • Well, who am I? If I make any wrong speculation and this results in you looking in the wrong places until then one day you realize how that punk on SO tricked you into this, no. thanks ;) -- btw. reading from top to bottom and now after done, wondering if the first code really is the start of some of the later mentioned includes is in? and session_start(); produces the session cookies and other session related headers. If you're looking for some (debugging) control, see this answer: https://stackoverflow.com/a/76595912/367456 (not your answer, only informative for session_start([...])) – hakre Jul 31 '23 at 22:32
  • So is it right that the session id stays the same? log the return value of session_start() and tell whether it is true or false. (I also don't understand the if clause around it, but perhaps I must not). – hakre Jul 31 '23 at 22:54
  • 1
    There is 1. *"but on every page load the PHP system doesn't find the existing session file*" and 2. *"PHP Session id's are not being transferred between pages"*. Is the session lost on the same page (1) or only when navigating to another page (2)? Did you try to include only `header.php`? That would ease debug and reduce the size of this Q. Does it work with `domain.uk` instead of `.domain.uk`? (the PHP doc is unclear about that) – A.L Jul 31 '23 at 23:00
  • 1
    and perhaps unrelated, but I'm so much _not_ a fan of $_SERVER['DOCUMENT_ROOT'] for requires, I wonder if you can't replace it with `__DIR__` , it's always not clear if there is some dynamic loading necessary induced by different virtual hosts and then the domain changes .... . – hakre Jul 31 '23 at 23:02
  • 2
    There's too much noise here. Start with literally just a page with `session_start();var_dump(session_id());` and start adding bits until the ID changes. – rjdown Jul 31 '23 at 23:29
  • @A.L please see the bottom of my question for update on the issue and answers to the points you raised in your comments – Martin Aug 01 '23 at 11:03
  • @hakre please see the updates to my question, I have established this morning the cause is browser specific , which is actually even more surprising. – Martin Aug 01 '23 at 11:04
  • 2
    What happens when you turn Firefox' "Enhanced Tracking Protection" off for the site ? – CBroe Aug 01 '23 at 11:06
  • @CBroe Ok correction: It does work; I have disabled ETP for the website *and then I cleared the website history and the cache* and reloaded and now I can log in. Thanks for the pointer. Please put this in an answer `:-)` – Martin Aug 01 '23 at 11:13
  • And: If you mark the cookie as session cookie, does it work then with ETP enabled? (set the cookie.lifetime to 0 [currently 4000]) and if you need that limit, maybe session garbage collector settings or you handle by timestamps in the session data itself, revalidating them after start. – hakre Aug 01 '23 at 11:37
  • 1
    @hakre tbh the 4000 was just a test value, the original cookie lifetime value was 0 – Martin Aug 01 '23 at 11:43

1 Answers1

3

Problems with cookie-based sessions, that only manifest in Firefox, are likely connected to the Enhanced Tracking Protection that Firefox introduced a while back (June 2019; Firefox 67.

If only turning this off for yourself in your local browser already "fixes" the problem to the necessary degree for you, then that can be considered the solution here, I suppose. If you need this to work for other users as well - then you will probably have to instruct them to also turn this off, if they run into problems - or investigate, if there is a way to avoid this for legitimate use cases. (I don't know too much about how it works, whether it is based on a list of known "trackers", or some heuristic approach - or probably a combination of both.)

hakre
  • 193,403
  • 52
  • 435
  • 836
CBroe
  • 91,630
  • 14
  • 92
  • 150
  • 1
    I will be recommending to the client that their staff use Chrome or non-Firefox browser. Which I think most do anyway. I can't believe after all my efforts yesterday it's a browser issue rather than a server side issue. I s'pose that's good overall, however. – Martin Aug 01 '23 at 11:27
  • @Martin: I also edited in a link to the Mozilla Knowledge Base, it may also offer additional helpful information for the client. – hakre Aug 01 '23 at 11:28
  • Previously PHP Sessions were absolutely fine so I think the issue came about with a[ny] recent update to ETP on FF115 or similar, – Martin Aug 01 '23 at 11:29
  • That could be, there is a second version ([Firefox 79; Aug 2020](https://blog.mozilla.org/security/2020/08/04/firefox-79-includes-protections-against-redirect-tracking/)) but I know too little about it pin-point a more recent change (I can imagine though, that there are some from time to time, perhaps search FF sources for the domain name? ^^). – hakre Aug 01 '23 at 11:33
  • I wonder what's going on here. I use Firefox as my daily driver (though it already got migrated to 116) and PHP sessions work fine with tracking protection enabled for all sites I maintain or use. Is the problem specific to .uk TLD? – Álvaro González Aug 01 '23 at 12:03
  • @ÁlvaroGonzález I come to a conclusion the issue is an update in the 115 firefox tweaking how the ETP works. However, I use very similar systems to this on other sites and they've been fine, so I'm not sure the root cause in this case. – Martin Aug 02 '23 at 10:07