FrontAccounting 2.4.20 on PHP 8.3, site published through Cloudflare with the proxy on (orange cloud).
Problem:
The login was dropped every few screens, and in particular each time a main-menu tab was opened (index.php?application=...). Logging in again worked, until the next tab.
SessionManager stores the client address on the session and, in preventHijacking(), treats a different address as a hijack and wipes the session. Behind Cloudflare, REMOTE_ADDR is a Cloudflare edge address, and that address changes from one request to the next. The visitor has not changed. The next menu click looks like a hijack, and the login is cleared.
Solution:
I enabled a solution that works for all users as it currently does, but also for servers who use Cloudflare Orange.
What I changed:
1. The address stored and compared is the visitor address Cloudflare sends (CF-Connecting-IP), and only when the request also has Cloudflare's CF-RAY header. Both headers are required so a client who simply sends CF-Connecting-IP is not trusted.
Users who are not coming through Cloudflare orange-cloud, or through another proxy that sets those two headers, are not affected. A LAN client, or a WAN client hitting the server directly, has no CF-RAY header, so clientAddress() returns REMOTE_ADDR and the check is the same as before.
2. I removed the 5% random call to regenerateSession() on every request. That path is not specific to Cloudflare. On PHP 8, regenerateSession() (session_regenerate_id, session_write_close, then session_start again) drops the logged-in session on the requests where it runs. A real address mismatch still calls regenerateSession().
Code:
------
Replaced, in sessionStart():
$_SESSION['IPaddress'] = $_SERVER['REMOTE_ADDR'];
with:
$_SESSION['IPaddress'] = $this->clientAddress();
------
Removed, immediately after regenerateSession() in that same block:
// Give a 5% chance of the session id changing on any request
}
elseif (rand(1, 100) <= 5)
{
$this->regenerateSession();
}
------
Replaced, in preventHijacking():
if ($_SESSION['IPaddress'] != $_SERVER['REMOTE_ADDR'])
return false;
with:
if ($_SESSION['IPaddress'] != $this->clientAddress())
return false;
------
Added, immediately above preventHijacking():
// Visitor address. Cloudflare's own address changes between requests, so the
// session is kept against CF-Connecting-IP when that request came through Cloudflare.
function clientAddress()
{
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP']) && !empty($_SERVER['HTTP_CF_RAY']))
return $_SERVER['HTTP_CF_CONNECTING_IP'];
return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
}
------
After this, one fresh login stays put across the menu tabs. I'm happy for this to be adjusted if there is a preferred way to handle it in the next release.
