Expiration of fat-free framework sessions - php

How do i set a fat-free framework session to expire after a duration, say 5 minutes, of inactivity? My web app keeps open until a user logouts out.

First of all you need to know about the PHP session garbage collector behaviour. By default, it triggers randomly on every 100th request (for performance purposes), looks for expired session files (default: 1440s) and remove them.
Also you need to know that some Linux distributions (e.g Debian/Ubuntu) disable this garbage collector and replace it with their own cron job.
You could start checking your PHP configuration:
foreach (['gc_probability','gc_divisor','gc_maxlifetime'] as $k)
echo $k,'=',ini_get("session.$k"),'<br>';
If the GC probability is 0, sessions files won't ever be removed (or could be removed by a cron job on Debian/Ubuntu). If it is not 0, but is low (e.g 1/100), sessions files will be removed some time (try to refresh the page 100 times).
In theory, you could set the probability to 1 (gc_probability=gc_divisor=1) to have sessions files removed as soon as they get expired. That would work on small apps with low traffic, but would affect performance on bigger apps (imagine that the GC needs to scan 1000 or more session files on each request).
The cleanest and most portable way of handle this issue is to expire sessions yourself. Every time you're loading a user session data, check the last time it was here and clear the session data if it's expired.
Here's a small example:
$f3->TIMEOUT=7200;// define session timeout here (in seconds)
ini_set('session.gc_maxlifetime',$f3->TIMEOUT);// see note (*) below
ini_set('session.cookie_lifetime',$f3->TIMEOUT);// optional (**)
$f3->route('GET|POST|DELETE /session',function($f3){
// load session data
$data=&$f3->ref('SESSION.data');
// sign in on POST requests
if ($f3->VERB==='POST') {
$data=['user'=>'John','stamp'=>time()];
$f3->reroute();
}
// sign out on DELETE requests
if ($f3->VERB==='DELETE') {
// sign out
$data=NULL;
$f3->reroute();
}
// check if session has expired
if (is_array($data) && time()>$data['stamp']+$f3->TIMEOUT) {
$data=NULL;
}
// check if user is authenticated
if (is_array($data)) {
echo 'Welcome ',$data['user'],' last time we\'ve seen you was ',date(DATE_W3C,$data['stamp']);
echo '<form action="" method="post"><button>Sign out</button><input type="hidden" name="_method" value="DELETE"/></form>';
$data['stamp']=time();// update session stamp
} else
echo 'You\'re not authenticated';
echo '<form action="" method="post"><button>Authenticate as `John`</button></form>';
});
Of course, you'd better wrap all this logic in a dedicated class.
(*) PHP GC maxlifetime should not be lower than $f3->TIMEOUT, otherwise it could interfere with it. Let's say $f3->TIMEOUT equals 7200 and session.gc_maxlifetime is set to 1440 (default), there are chances that your user sessions get expired between 1440 and 7200. NB: On Debian/Ubuntu, you should set this parameter inside php.ini otherwise the cron job cleaning up session files won't be aware of it.
(**) If you skip this line, the session cookie lifetime defaults to 0, which means "until the browser is closed". See here.

Related

Disconnect if a PHP page remains inactive for 5 minutes [duplicate]

I would like to extend the session timeout in php
I know that it is possible to do so by modifying the php.ini file.
But I don't have access to it.
So is it possible to do it only with php code?
Session timeout is a notion that has to be implemented in code if you want strict guarantees; that's the only way you can be absolutely certain that no session ever will survive after X minutes of inactivity.
If relaxing this requirement a little is acceptable and you are fine with placing a lower bound instead of a strict limit to the duration, you can do so easily and without writing custom logic.
Convenience in relaxed environments: how and why
If your sessions are implemented with cookies (which they probably are), and if the clients are not malicious, you can set an upper bound on the session duration by tweaking certain parameters. If you are using PHP's default session handling with cookies, setting session.gc_maxlifetime along with session_set_cookie_params should work for you like this:
// server should keep session data for AT LEAST 1 hour
ini_set('session.gc_maxlifetime', 3600);
// each client should remember their session id for EXACTLY 1 hour
session_set_cookie_params(3600);
session_start(); // ready to go!
This works by configuring the server to keep session data around for at least one hour of inactivity and instructing your clients that they should "forget" their session id after the same time span. Both of these steps are required to achieve the expected result.
If you don't tell the clients to forget their session id after an hour (or if the clients are malicious and choose to ignore your instructions) they will keep using the same session id and its effective duration will be non-deterministic. That is because sessions whose lifetime has expired on the server side are not garbage-collected immediately but only whenever the session GC kicks in.
GC is a potentially expensive process, so typically the probability is rather small or even zero (a website getting huge numbers of hits will probably forgo probabilistic GC entirely and schedule it to happen in the background every X minutes). In both cases (assuming non-cooperating clients) the lower bound for effective session lifetimes will be session.gc_maxlifetime, but the upper bound will be unpredictable.
If you don't set session.gc_maxlifetime to the same time span then the server might discard idle session data earlier than that; in this case, a client that still remembers their session id will present it but the server will find no data associated with that session, effectively behaving as if the session had just started.
Certainty in critical environments
You can make things completely controllable by using custom logic to also place an upper bound on session inactivity; together with the lower bound from above this results in a strict setting.
Do this by saving the upper bound together with the rest of the session data:
session_start(); // ready to go!
$now = time();
if (isset($_SESSION['discard_after']) && $now > $_SESSION['discard_after']) {
// this session has worn out its welcome; kill it and start a brand new one
session_unset();
session_destroy();
session_start();
}
// either new or old, it should live at most for another hour
$_SESSION['discard_after'] = $now + 3600;
Session id persistence
So far we have not been concerned at all with the exact values of each session id, only with the requirement that the data should exist as long as we need them to. Be aware that in the (unlikely) case that session ids matter to you, care must be taken to regenerate them with session_regenerate_id when required.
If you use PHP's default session handling, the only way to reliably change the session duration in all platforms is to change php.ini. That's because in some platforms, garbage collection is implemented through a script that runs every certain time (a cron script) that reads directly from php.ini, and therefore any attempts at changing it at run time, e.g. via ini_set(), are unreliable and most likely won't work.
For example, in Debian Linux systems, PHP's internal garbage collection is disabled by setting session.gc_probability=0 by default in the configuration, and is instead done via /etc/cron.d/php, which runs at XX:09 and XX:39 (that is, every half hour). This cron job looks for sessions older than the session.gc_maxlifetime specified in the configuration, and if any are found, they are deleted. As a consequence, in these systems ini_set('session.gc_maxlifetime', ...) is ignored. That also explains why in this question: PHP sessions timing out too quickly, the OP had problems in one host but the problems ceased when switching to a different host.
So, given that you don't have access to php.ini, if you want to do it portably, using the default session handling is not an option. Apparently, extending the cookie lifetime was enough for your host, but if you want a solution that works reliably even if you switch hosts, you have to use a different alternative.
Available alternative methods include:
Set a different session (save) handler in PHP to save your sessions in a different directory or in a database, as specified in PHP: Custom Session Handlers (PHP manual), so that the cron job doesn't reach it, and only PHP's internal garbage collection takes place. This option probably can make use of ini_set() to set session.gc_maxlifetime but I prefer to just ignore the maxlifetime parameter in my gc() callback and determine maximum lifetime on my own.
Completely forget about PHP internal session handling and implement your own session management. This method has two main disadvantages: you will need your own global session variables, so you lose the advantage of the $_SESSION superglobal, and it needs more code thus there are more opportunities for bugs and security flaws. Most importantly, the session identifier should be generated out of cryptographically secure random or pseudorandom numbers to avoid session ID predictability (leading to possible session hijacking), and that is not so easy to do with PHP portably. The main advantage is that it will work consistently in all platforms and you have full control over the code. That's the approach taken e.g. by the phpBB forum software (at least version 1; I'm not sure about more recent versions).
There is an example of (1) in the documentation for session_set_save_handler(). The example is long but I'll reproduce it here, with the relevant modifications necessary to extend the session duration. Note the inclusion of session_set_cookie_params() to increase the cookie lifetime as well.
<?php
class FileSessionHandler
{
private $savePath;
private $lifetime;
function open($savePath, $sessionName)
{
$this->savePath = 'my_savepath'; // Ignore savepath and use our own to keep it safe from automatic GC
$this->lifetime = 3600; // 1 hour minimum session duration
if (!is_dir($this->savePath)) {
mkdir($this->savePath, 0777);
}
return true;
}
function close()
{
return true;
}
function read($id)
{
return (string)#file_get_contents("$this->savePath/sess_$id");
}
function write($id, $data)
{
return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
}
function destroy($id)
{
$file = "$this->savePath/sess_$id";
if (file_exists($file)) {
unlink($file);
}
return true;
}
function gc($maxlifetime)
{
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $this->lifetime < time() && file_exists($file)) { // Use our own lifetime
unlink($file);
}
}
return true;
}
}
$handler = new FileSessionHandler();
session_set_save_handler(
array($handler, 'open'),
array($handler, 'close'),
array($handler, 'read'),
array($handler, 'write'),
array($handler, 'destroy'),
array($handler, 'gc')
);
// the following prevents unexpected effects when using objects as save handlers
register_shutdown_function('session_write_close');
session_set_cookie_params(3600); // Set session cookie duration to 1 hour
session_start();
// proceed to set and retrieve values by key from $_SESSION
Approach (2) is more complicated; basically, you have to re-implement all session functions on your own. I won't go into details here.
Just a notice for a sharing hosting server or added on domains =
For your settings to work you must have a different save session dir for added domain by using php_value session.save_path folderA/sessionsA.
So create a folder to your root server, not into the public_html and not to be publicity accessed from outside. For my cpanel/server worked fine the folder permissions 0700. Give a try...
# Session timeout, 2628000 sec = 1 month, 604800 = 1 week, 57600 = 16 hours, 86400 = 1 day
ini_set('session.save_path', '/home/server/.folderA_sessionsA');
ini_set('session.gc_maxlifetime', 57600);
ini_set('session.cookie_lifetime', 57600);
# session.cache_expire is in minutes unlike the other settings above
ini_set('session.cache_expire', 960);
ini_set('session.name', 'MyDomainA');
before session_start();
or put this in your .htaccess file.
php_value session.save_path /home/server/.folderA_sessionsA
php_value session.gc_maxlifetime 57600
php_value session.cookie_lifetime 57600
php_value session.cache_expire 57600
php_value session.name MyDomainA
After many researching and testing this worked fine for shared cpanel/php7 server. Many thanks to: NoiS
Adding comment for anyone using Plesk having issues with any of the above as it was driving me crazy, setting session.gc_maxlifetime from your PHP script wont work as Plesk has it's own garbage collection script run from cron.
I used the solution posted on the link below of moving the cron job from hourly to daily to avoid this issue, then the top answer above should work:
mv /etc/cron.hourly/plesk-php-cleanuper /etc/cron.daily/
https://websavers.ca/plesk-php-sessions-timing-earlier-expected
Put $_SESSION['login_time'] = time(); into the previous authentication page.
And the snipped below in every other page where you want to check the session time-out.
if(time() - $_SESSION['login_time'] >= 1800){
session_destroy(); // destroy session.
header("Location: logout.php");
die(); // See https://thedailywtf.com/articles/WellIntentioned-Destruction
//redirect if the page is inactive for 30 minutes
}
else {
$_SESSION['login_time'] = time();
// update 'login_time' to the last time a page containing this code was accessed.
}
Edit : This only works if you already used the tweaks in other posts, or disabled Garbage Collection, and want to manually check the session duration.
Don't forget to add die() after a redirect, because some scripts/robots might ignore it. Also, directly destroying the session with session_destroy() instead of relying on a redirect for that might be a better option, again, in case of a malicious client or a robot.
No.
If you don't have access to the php.ini, you can't guarantee that changes would have any effect.
I doubt you need to extend your sessions time though.
It has pretty sensible timeout at the moment and there are no reasons to extend it.
You can override values in php.ini from your PHP code using ini_set().

How in adminer to increase current session on login?

Using adminer-4.7.7 with plugin login-password-less.php
I found a way how to enter adminer without credentials entered.
But entering adminer I would like to keep the current session as far as possible(including
current db and table opened), even after browse closed/opened next day...
Has adminer session time parameter/tools?
That is for my home laptop ( kununtu18, apache 2, php 7.4), so security breaks are ignored...
apache config decisions also possible.
How can I do it ?
MODIFIED :
I tried to increase gc_maxlifetime as in Jasar Orion code, but failed
I modified session block in adminer/include/bootstrap.inc.php as:
global $adminer, $connection, $driver, $drivers, $edit_functions, $enum_length, $error, $functions, $grouping, $HTTPS, $inout, $jush, $LANG, $langs, $on_actions, $permanent, $structured_types, $has_token, $token, $translations, $types, $unsigned, $VERSION; // allows including Adminer inside a function
if (!$_SERVER["REQUEST_URI"]) { // IIS 5 compatibility
$_SERVER["REQUEST_URI"] = $_SERVER["ORIG_PATH_INFO"];
}
if (!strpos($_SERVER["REQUEST_URI"], '?') && $_SERVER["QUERY_STRING"] != "") { // IIS 7 compatibility
$_SERVER["REQUEST_URI"] .= "?$_SERVER[QUERY_STRING]";
}
if ($_SERVER["HTTP_X_FORWARDED_PREFIX"]) {
$_SERVER["REQUEST_URI"] = $_SERVER["HTTP_X_FORWARDED_PREFIX"] . $_SERVER["REQUEST_URI"];
}
$HTTPS = ($_SERVER["HTTPS"] && strcasecmp($_SERVER["HTTPS"], "off")) || ini_bool("session.cookie_secure"); // session.cookie_secure could be set on HTTP if we are behind a reverse proxy
#ini_set("session.use_trans_sid", false); // protect links in export, # - may be disabled
if (!defined("SID")) {
session_cache_limiter(""); // to allow restarting session
session_name("adminer_sid"); // use specific session name to get own namespace
// $params = array(0, preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]), "", $HTTPS);
if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
$params[] = true; // HttpOnly
}
$lifetime = time() + 97 * 24 * 60 * 60;
$params = array($lifetime, preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]), "", $HTTPS);
ini_set('session.gc_maxlifetime', 99600);
call_user_func_array('session_set_cookie_params', $params); // ini_set() may be disabled
session_set_cookie_params(99600);
session_start();
}
It is invalid way? Which way is valid?
Thanks!
If you open file adminer/adminer/include/bootstrap.inc.php:57 you could see the following code:
call_user_func_array('session_set_cookie_params', $params); // ini_set() may be disabled
According to documentation this methods sets session lifetime. By default it set to 0, which means that session will be destroyed when browser is closed.
I suppose, you could set another value you want:
$lifetime = time() + 7 * 24 * 60 * 60;
$params = array($lifetime, preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]), "", $HTTPS);
This could be a problem of permission,
can you check in your phpinfo() the value of “session.save_path”, if you are working on localhost this is normally C:\xampp\tmp.
If you are working on ubuntu the session folder should be /var/lib/php/session, if the folder session doesn't exist you should navigate to /var/lib/php/ and create the folder with:
mkdir session
then you need to extend the permission to user www-data, with:
sudo chown -R www-data:www-data /var/lib/php/session
Good luck!
Session timeout is a notion that has to be implemented in code if you want strict guarantees; that's the only way you can be absolutely certain that no session ever will survive after X minutes of inactivity.
If relaxing this requirement a little is acceptable and you are fine with placing a lower bound instead of a strict limit to the duration, you can do so easily and without writing custom logic.
Convenience in relaxed environments: how and why
If your sessions are implemented with cookies (which they probably are), and if the clients are not malicious, you can set an upper bound on the session duration by tweaking certain parameters. If you are using PHP's default session handling with cookies, setting session.gc_maxlifetime along with session_set_cookie_params should work for you like this:
// server should keep session data for AT LEAST 1 hour
ini_set('session.gc_maxlifetime', 3600);
// each client should remember their session id for EXACTLY 1 hour
session_set_cookie_params(3600);
session_start(); // ready to go!
This works by configuring the server to keep session data around for at least one hour of inactivity and instructing your clients that they should "forget" their session id after the same time span. Both of these steps are required to achieve the expected result.
If you don't tell the clients to forget their session id after an hour (or if the clients are malicious and choose to ignore your instructions) they will keep using the same session id and its effective duration will be non-deterministic. That is because sessions whose lifetime has expired on the server side are not garbage-collected immediately but only whenever the session GC kicks in.
GC is a potentially expensive process, so typically the probability is rather small or even zero (a website getting huge numbers of hits will probably forgo probabilistic GC entirely and schedule it to happen in the background every X minutes). In both cases (assuming non-cooperating clients) the lower bound for effective session lifetimes will be session.gc_maxlifetime, but the upper bound will be unpredictable.
If you don't set session.gc_maxlifetime to the same time span then the server might discard idle session data earlier than that; in this case, a client that still remembers their session id will present it but the server will find no data associated with that session, effectively behaving as if the session had just started.
Certainty in critical environments
You can make things completely controllable by using custom logic to also place an upper bound on session inactivity; together with the lower bound from above this results in a strict setting.
Do this by saving the upper bound together with the rest of the session data:
session_start(); // ready to go!
$now = time();
if (isset($_SESSION['discard_after']) && $now > $_SESSION['discard_after']) {
// this session has worn out its welcome; kill it and start a brand new one
session_unset();
session_destroy();
session_start();
}
// either new or old, it should live at most for another hour
$_SESSION['discard_after'] = $now + 3600;
Session id persistence
So far we have not been concerned at all with the exact values of each session id, only with the requirement that the data should exist as long as we need them to. Be aware that in the (unlikely) case that session ids matter to you, care must be taken to regenerate them with session_regenerate_id when required.

What is the best way to make "remember me" under php when using native sessions?

Previously i was creating additional cookie "rememberme" with unique hash, that was stored in the database, mapped to the user id.
If user had such cookie - website tried to find it's value in database, and if it was found session was setting up.
Later, developing new project i thought that it is maybe not very secure to generate this unique hash by myself, and keeping two cookies (native "PHPSESSID" + my "rememberme") for one operation (user identification) is overkill.
Maybe there is a way to setup not global session lifetime, but to setup it individually for different user sessions... or maybe it is better to keep user sessions in the database, mapped to the userid?
UPDATE 1
I thought if it is so hard to make "remember me" button, we can go another way - to make "Not my computer button". Idea is to set default cookie_lifetime for a week in php.ini (for example), and if user checkes this checkbox - we will set cookie_lifetime into zero using session_set_cookie_params function.
So, 1st question is - will session_set_cookie_params affect other users cookies (in documentation it is said, that session_set_cookie_params options will have effect until php process will be executing)
2d question is that if session_set_cookie_params is not affecting global settings, will session regeneration affect users, that don't want to keep a long-life cookie?
UPDATE 2: [Question 1 answer]
Just tested session_set_cookie_params function.
I wrote a script, that sets session cookie lifetime into zero using session_set_cookie_params and then executing for 30 seconds:
if ($_GET['test']) {
session_set_cookie_params (0);
while (true) {
sleep(1);
}
}
session_start();
So, in first browser i just started this script with ?test=1 parameter, just after that (while this script was executing) i started this script without parameters in the second browser. The answer is no - second browser's cookie was not affected. It had lifetime, that was specified in php.ini
UPDATE 3: [Question 2 answer]
Then, i've tried to check if regeneration affects session cookie lifetime, that was set by session_set_cookie_params.
Yes, it affects. If i set session cookie with customized lifetime, that was set by session_set_cookie_params, and then call session_regenerate_id(), cookie will have lifetime, set in php.ini
But, if we set session_set_cookie_params (0) before calling session_regenerate_id(), our cookie will have correct lifetime.
So, that's it! That was easy! 8)
Thank you, ladies and gentlemen!
If you want to do this only using sessions you can do the following if the user wants to be remembered:
if((isset($_POST['remember_me']) && $_POST['remember_me']) || ($_COOKIE['remember_me']) && $_COOKIE['remember_me'])) {
// store these cookies in an other directory to make sure they don't
// get deleted by the garbage collector when starting a "non-remeber-me"-session
$remember_me_dir = ini_get('session.save_path') . DS . "remember_me_sessions";
// create the directory if it doesn't exist
if (!is_dir($remember_me_dir)) {
mkdir($remember_me_dir);
}
// set the php.ini-directive (temporarily)
ini_set('session.save_path', $remember_me_dir);
// define lifetime of the cookie on client side
$expire_cookie = 60 * 60 * 24 * 30; // in seconds
session_set_cookie_params($expire_cookie);
// lifetime of the cookie on server side
// session file gets deleted after this timespan
// add a few seconds to make sure the browser deletes
// the cookie first.
$garbage_in = $expire_cookie + 600; // in seconds
// set the php-ini directive for the garbage collector of the session files.
ini_set('session.gc_maxlifetime', $garbage_in);
// send an additional cookie to keep track of the users
// which checked the 'remember_me' checkbox
setcookie('remember_me', 1, time() + $expire_cookie);
}
// now we are ready to start the session
// For all the users which didn't choose to check the 'remember_me' box
// the default settings in php.ini are used.
session_start();
Here you can read more about the session related php.ini-directives
As it was so hard to make "remember me" checkbox functionality, i came to another way, using only one cookie.
PREPARATION
1) I've prepared a form with three inputs:
"login" input [type=text]: user's login
"password" input [type=password]: user's password
"not my computer" input [type=checkbox]: that will tell us to use session cookie with lifetime = 0 (cookie must be deleted when browser will be closed)
2) I've set session.cookie_lifetime = 100500 to keep long-life cookies by default.
COOKIE SETUP
So, after user submits the form, we check - if he has selected to use short sessions - we call session_set_cookie_params(0) before setting session cookie to him (before actually using session_start()).
COOKIE REGENERATION
Then, when we need to regenerate session cookie, we can also do this easily with session_regenerate_id() function.
But we need to remember, that this function will re-set session cookie lifetime from php.ini by default.
So, we need also to call session_set_cookie_params() before regenerating a cookie.
BTW, You can store custom session cookie lifetime in $_SESSION.
It will look like this:
// Form handling, session setup
if ($_POST['not-my-computer']) {
session_set_cookie_params(0);
session_start();
$_SESSION['expires'] = 0;
}
// Session regeneration
if (isset($_SESSION['expires'])) {
session_set_cookie_params(0);
session_regenerate_id();
}
Details for this answer (and more deep explanations) you can find in the question text (while i was testing, i added answers/tests results there)

Does session.gc_maxlifetime specify the maximum lifetime since the last change of a single session variable?

I'm using a java based uploading construct http://www.javaatwork.com/java-upload-applet/details.html that I tried running over night.
It basically stores everything on the server's hard drive (/var/www/private/$userId)
Makes sure that data is well-formed
Then passes it onto a permanent storage (Amazon S3).
After step 1 completes, I run the following code:
if($_SESSION['userId'])
{//Makes sure that data is well-formed}
else
{echo 'you are not logged in';}
I tried running this for four hours only to find you are not logged in printed to the screen.
Here are the appropriate directives in the cgi php.ini file (I'm using ubuntu 12.04 with apache2.)
session.gc_probability = 0
session.gc_divisor = 1000
session.gc_maxlifetime = 14400 //this is 30 hours, which is far greater than the 4 hours it was running
session.cache_expire = 1800
session.cookie_lifetime = 0
Most of these directives are the default with exception to session.gc_probability and session.gc_maxlifetime.
I was trying to resolve this issue and came across a really helpful blog by Jeff from which I inferred that browsers can cause the PHPSESSID cookie stored in the browser to be deleted if a period of inactivity on the website from within the browser occurs. He suggests
"Create a background JavaScript process in the browser that sends regular heartbeats to the server. Regenerate a new cookie with timed expiration, say, every 5 or 10 minutes."
http://www.codinghorror.com/blog/2008/04/your-session-has-timed-out.html
So I decided to do just that.
function myTimeoutFunction()
{
$.ajax({
url: "heartbeat.php",
success: function() {
}
});
setTimeout(myTimeoutFunction, 15*60*1000);
}
myTimeoutFunction();
heartbeat.php
<?php session_start(); ?>
I'm about to test this for an upload that should take ~4 hours. However I just read the following
In general you can say session.gc_maxlifetime specifies the maximum lifetime since the last change of your session data (not the last time session_start was called)
https://stackoverflow.com/a/1516338/784637
If I had 3 session variables, $_SESSION['userId'] $_SESSION['firstName'] $_SESSION['lastName'], would I need to reset all their values in heartbeat.php
session_start();
$_SESSION['userId'] = $_SESSION['userId'];
$_SESSION['firstName'] = $_SESSION['firstName'];
$_SESSION['lastName'] = $_SESSION['lastName'];
Or could I just reset one value
session_start();
$_SESSION['lastHeartbeat'] = time();
so that the other three would not expire?
The PHP session is kept as a whole; any changes in $_SESSION will update the change time and, by extensions, preserve the entire session.
Concerning the actual issue: PHP shouldn't GC sessions until the max time is reached, but that doesn't mean PHP is always clearing it. By default, sessions are kept in the /tmp (or another) directory and some Linux distros will have cron jobs that may clean the folder out from time to tome. Check for crons or other things that may clear the sessions independent of PHP too.

How to change the session timeout in PHP?

I would like to extend the session timeout in php
I know that it is possible to do so by modifying the php.ini file.
But I don't have access to it.
So is it possible to do it only with php code?
Session timeout is a notion that has to be implemented in code if you want strict guarantees; that's the only way you can be absolutely certain that no session ever will survive after X minutes of inactivity.
If relaxing this requirement a little is acceptable and you are fine with placing a lower bound instead of a strict limit to the duration, you can do so easily and without writing custom logic.
Convenience in relaxed environments: how and why
If your sessions are implemented with cookies (which they probably are), and if the clients are not malicious, you can set an upper bound on the session duration by tweaking certain parameters. If you are using PHP's default session handling with cookies, setting session.gc_maxlifetime along with session_set_cookie_params should work for you like this:
// server should keep session data for AT LEAST 1 hour
ini_set('session.gc_maxlifetime', 3600);
// each client should remember their session id for EXACTLY 1 hour
session_set_cookie_params(3600);
session_start(); // ready to go!
This works by configuring the server to keep session data around for at least one hour of inactivity and instructing your clients that they should "forget" their session id after the same time span. Both of these steps are required to achieve the expected result.
If you don't tell the clients to forget their session id after an hour (or if the clients are malicious and choose to ignore your instructions) they will keep using the same session id and its effective duration will be non-deterministic. That is because sessions whose lifetime has expired on the server side are not garbage-collected immediately but only whenever the session GC kicks in.
GC is a potentially expensive process, so typically the probability is rather small or even zero (a website getting huge numbers of hits will probably forgo probabilistic GC entirely and schedule it to happen in the background every X minutes). In both cases (assuming non-cooperating clients) the lower bound for effective session lifetimes will be session.gc_maxlifetime, but the upper bound will be unpredictable.
If you don't set session.gc_maxlifetime to the same time span then the server might discard idle session data earlier than that; in this case, a client that still remembers their session id will present it but the server will find no data associated with that session, effectively behaving as if the session had just started.
Certainty in critical environments
You can make things completely controllable by using custom logic to also place an upper bound on session inactivity; together with the lower bound from above this results in a strict setting.
Do this by saving the upper bound together with the rest of the session data:
session_start(); // ready to go!
$now = time();
if (isset($_SESSION['discard_after']) && $now > $_SESSION['discard_after']) {
// this session has worn out its welcome; kill it and start a brand new one
session_unset();
session_destroy();
session_start();
}
// either new or old, it should live at most for another hour
$_SESSION['discard_after'] = $now + 3600;
Session id persistence
So far we have not been concerned at all with the exact values of each session id, only with the requirement that the data should exist as long as we need them to. Be aware that in the (unlikely) case that session ids matter to you, care must be taken to regenerate them with session_regenerate_id when required.
If you use PHP's default session handling, the only way to reliably change the session duration in all platforms is to change php.ini. That's because in some platforms, garbage collection is implemented through a script that runs every certain time (a cron script) that reads directly from php.ini, and therefore any attempts at changing it at run time, e.g. via ini_set(), are unreliable and most likely won't work.
For example, in Debian Linux systems, PHP's internal garbage collection is disabled by setting session.gc_probability=0 by default in the configuration, and is instead done via /etc/cron.d/php, which runs at XX:09 and XX:39 (that is, every half hour). This cron job looks for sessions older than the session.gc_maxlifetime specified in the configuration, and if any are found, they are deleted. As a consequence, in these systems ini_set('session.gc_maxlifetime', ...) is ignored. That also explains why in this question: PHP sessions timing out too quickly, the OP had problems in one host but the problems ceased when switching to a different host.
So, given that you don't have access to php.ini, if you want to do it portably, using the default session handling is not an option. Apparently, extending the cookie lifetime was enough for your host, but if you want a solution that works reliably even if you switch hosts, you have to use a different alternative.
Available alternative methods include:
Set a different session (save) handler in PHP to save your sessions in a different directory or in a database, as specified in PHP: Custom Session Handlers (PHP manual), so that the cron job doesn't reach it, and only PHP's internal garbage collection takes place. This option probably can make use of ini_set() to set session.gc_maxlifetime but I prefer to just ignore the maxlifetime parameter in my gc() callback and determine maximum lifetime on my own.
Completely forget about PHP internal session handling and implement your own session management. This method has two main disadvantages: you will need your own global session variables, so you lose the advantage of the $_SESSION superglobal, and it needs more code thus there are more opportunities for bugs and security flaws. Most importantly, the session identifier should be generated out of cryptographically secure random or pseudorandom numbers to avoid session ID predictability (leading to possible session hijacking), and that is not so easy to do with PHP portably. The main advantage is that it will work consistently in all platforms and you have full control over the code. That's the approach taken e.g. by the phpBB forum software (at least version 1; I'm not sure about more recent versions).
There is an example of (1) in the documentation for session_set_save_handler(). The example is long but I'll reproduce it here, with the relevant modifications necessary to extend the session duration. Note the inclusion of session_set_cookie_params() to increase the cookie lifetime as well.
<?php
class FileSessionHandler
{
private $savePath;
private $lifetime;
function open($savePath, $sessionName)
{
$this->savePath = 'my_savepath'; // Ignore savepath and use our own to keep it safe from automatic GC
$this->lifetime = 3600; // 1 hour minimum session duration
if (!is_dir($this->savePath)) {
mkdir($this->savePath, 0777);
}
return true;
}
function close()
{
return true;
}
function read($id)
{
return (string)#file_get_contents("$this->savePath/sess_$id");
}
function write($id, $data)
{
return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
}
function destroy($id)
{
$file = "$this->savePath/sess_$id";
if (file_exists($file)) {
unlink($file);
}
return true;
}
function gc($maxlifetime)
{
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $this->lifetime < time() && file_exists($file)) { // Use our own lifetime
unlink($file);
}
}
return true;
}
}
$handler = new FileSessionHandler();
session_set_save_handler(
array($handler, 'open'),
array($handler, 'close'),
array($handler, 'read'),
array($handler, 'write'),
array($handler, 'destroy'),
array($handler, 'gc')
);
// the following prevents unexpected effects when using objects as save handlers
register_shutdown_function('session_write_close');
session_set_cookie_params(3600); // Set session cookie duration to 1 hour
session_start();
// proceed to set and retrieve values by key from $_SESSION
Approach (2) is more complicated; basically, you have to re-implement all session functions on your own. I won't go into details here.
Just a notice for a sharing hosting server or added on domains =
For your settings to work you must have a different save session dir for added domain by using php_value session.save_path folderA/sessionsA.
So create a folder to your root server, not into the public_html and not to be publicity accessed from outside. For my cpanel/server worked fine the folder permissions 0700. Give a try...
# Session timeout, 2628000 sec = 1 month, 604800 = 1 week, 57600 = 16 hours, 86400 = 1 day
ini_set('session.save_path', '/home/server/.folderA_sessionsA');
ini_set('session.gc_maxlifetime', 57600);
ini_set('session.cookie_lifetime', 57600);
# session.cache_expire is in minutes unlike the other settings above
ini_set('session.cache_expire', 960);
ini_set('session.name', 'MyDomainA');
before session_start();
or put this in your .htaccess file.
php_value session.save_path /home/server/.folderA_sessionsA
php_value session.gc_maxlifetime 57600
php_value session.cookie_lifetime 57600
php_value session.cache_expire 57600
php_value session.name MyDomainA
After many researching and testing this worked fine for shared cpanel/php7 server. Many thanks to: NoiS
Adding comment for anyone using Plesk having issues with any of the above as it was driving me crazy, setting session.gc_maxlifetime from your PHP script wont work as Plesk has it's own garbage collection script run from cron.
I used the solution posted on the link below of moving the cron job from hourly to daily to avoid this issue, then the top answer above should work:
mv /etc/cron.hourly/plesk-php-cleanuper /etc/cron.daily/
https://websavers.ca/plesk-php-sessions-timing-earlier-expected
Put $_SESSION['login_time'] = time(); into the previous authentication page.
And the snipped below in every other page where you want to check the session time-out.
if(time() - $_SESSION['login_time'] >= 1800){
session_destroy(); // destroy session.
header("Location: logout.php");
die(); // See https://thedailywtf.com/articles/WellIntentioned-Destruction
//redirect if the page is inactive for 30 minutes
}
else {
$_SESSION['login_time'] = time();
// update 'login_time' to the last time a page containing this code was accessed.
}
Edit : This only works if you already used the tweaks in other posts, or disabled Garbage Collection, and want to manually check the session duration.
Don't forget to add die() after a redirect, because some scripts/robots might ignore it. Also, directly destroying the session with session_destroy() instead of relying on a redirect for that might be a better option, again, in case of a malicious client or a robot.
No.
If you don't have access to the php.ini, you can't guarantee that changes would have any effect.
I doubt you need to extend your sessions time though.
It has pretty sensible timeout at the moment and there are no reasons to extend it.
You can override values in php.ini from your PHP code using ini_set().

Categories