Okay so I've created a login system using PHP Sessions which stores user-related data within $_SESSION while logged in. To reach a PHP $_SESSION / session cookie whose expiry gets extended by x seconds every time the client refreshes a page, I created the following callback, which I call upon every page initiation:
<?php
if ( session_status() === PHP_SESSION_NONE ) {
session_start(
[
'cookie_path' => '/',
'cookie_domain' => 'mydomain.com',
'cookie_secure' => true,
'cookie_httponly' => true,
'cookie_samesite' => 'Strict',
'use_strict_mode' => true,
'use_trans_sid' => false,
'use_only_cookies' => true
]
);
} else {
// If session already exists, simply take it up again without overwriting parameters
session_start();
}
// Then determine the lifetime of the cookie (was only able to make the session cookie
// lifetime expendable using this syntax, as explained in the [first example of the php docs](https://www.php.net/manual/de/function.session-set-cookie-params.php)
setcookie(
session_name(),
session_id(),
[
'expires' => time() + x,
'path' => '/',
'domain' => 'mydomain.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]
);
?>
The reason why I specify all the:
httponly
secure
cookie path
cookie domain
samesite
parameters upon the very first call of session_start() AND also in the call of setcookie() is because if I specify one of them in session_start() and not in setcookie() or vice-versa, the browser returns two session cookies with the same session IDs, one having all of the mentioned flags, and the other without:
Now the problem is that, when I logout via the following callback, which I call as specified in the docs:
<?php
// Called via PHP Form Submit
session_start();
setcookie(
session_name(),
'',
time() - 42000,
'/',
'mydomain.com',
true,
true
);
session_destroy();
header( 'Location: mydomain.com' );
?>
I get the same problem as described in the images above; two session cookies in my browser, and again one having all the flags set, the other not, and the one without the flags set having set its expiry to the session's end, and the other one with its expiry set in x seconds; all exactly as in the image.
What am I doing wrong?
UPDATE
Is it may better to actually set all of the session cookie parameters via the php.ini file, and handle the session cookie expiry via a timestamp within $_SESSION, done like in this example?? Just thinking of a way of making the provision of any parameters in session_start() + any calls to setcookie() obsolete..
So my question is basically:
What's actually the best way of using several PHP session cookie flags, combined with a session expiry which is limited to let's say 10 mins, which gets refreshed by 10 mins on every page load?
Related
I have used setcookie in php to check users visiting my site. The thing is when i test it in my local server it works, the cookie gets set but when i upload the page in cpanel the cookie doesn't get set.
Below is a synopsis of my code:
<?php
session_start();
//set the cookie time to desired value;
setcookie("user", "abc", time()+3600);
//some other codes
if(!isset($_COOKIE["user"]))
{
//some other codes
}
?>
Any help will be much appreciated. Thanks
Regarding the answers to my questions in the comments, you probably just need to modify the cookie lifetime of your session and not create another "user" cookie.
// TTL (Time To Live) of the cookie stored in the browser.
ini_set('session.cookie_lifetime', 432000); // 5 days
// On the server side, the garbage collector should delete
// old sessions too, after the same TTL.
ini_set('session.gc_maxlifetime', 432000); // 5 days
// Fire the garbage collector only every 100 requests.
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
Do that at the beginning of your code, typically in a config.php file included at the bootstrap of your app.
You can see more in the PHP session configuration.
There's a little problem regarding the lifetime: the cookie will expire after the lifetime set, but since the cookie was created, so at the moment your session was initiated the first time at login.
Usually, you want the cookie lifetime to start at the last user's action. You can do that by updating the cookie so that PHP re-sends the Cookie HTTP header to overwrite it. I also added a bunch of other security settings one should do on PHP sessions. Everything is commented in the running example below:
<?php
/**
* Testing PHP cookie settings and improve security.
*/
// The cookie lifetime in seconds.
define('COOKIE_LIFETIME', 10);
// Simulate user's data from the database.
$user_mail = 'james.bond#gmail.com';
// A salt per user is good. It avoids an attacker to be able
// to calculate the session cookie name himself if he discovers that
// it is just done by hashing the user's e-mail.
$user_salt = 'nbVzr432';
// Detect if we are over HTTPS or not. Needs improvement if behind a SSL reverse-proxy.
// It's just used for the cookie secure option to make this POC work everywhere.
$is_https = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']);
// On the PHP server side, the garbage collector should delete
// old sessions after the same lifetime value.
ini_set('session.gc_maxlifetime', COOKIE_LIFETIME);
// Fire the garbage collector only every 100 requests to save CPU.
// On some OS this is done by a cron job so it could be commented.
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
// Improve some session settings:
ini_set('session.use_cookies ', 1);
ini_set('session.use_only_cookies ', 1);
ini_set('session.use_strict_mode ', 1);
// This POC will just print some plain text.
header('Content-Type: text/plain; charset=utf-8');
// Change the session cookie name so that an attacker cannot find it.
$session_cookie_name = 'SESS_' . sha1($user_mail . $user_salt);
// Store all the session cookie options in an array.
$session_cookie_options = [
// Set the cookie lifetime (the browser sets an expire to delete it automatically).
'lifetime' => COOKIE_LIFETIME,
// The cookie path defines under which relative URL the cookie should be sent.
// If your app is running under https://your-site.com/shop/ then the cookie path
// should be set to /shop/ instead of the default / as there's no reason to send
// your shop's session cookie to another app running at https://your-site.com/forum/.
'path' => '/',
// Cookie domain. Use null to let PHP handle that. But if you want a session
// cookie accross multiple sub-domains such as forum.your-site.com and shop.your-site.com
// then you should set the domain to ".your-site.com".
'domain' => null,
// If we are in HTTPS then don't let cookies be sent over HTTP.
// Here I used $is_https to make it run everywhere but if you have
// HTTPS on your domain then replace it by 1 to lock it!
'secure' => $is_https ? 1 : 0, // IMPORTANT: Replace by 1 if you have both HTTP and HTTPS enabled.
// Don't let JavaScript access the session cookie.
'httponly' => 1,
// If another site has a link pointing to your website then don't send
// the session cookie (POST or GET). This mitigates somes kind of attacks.
'samesite' => 'Strict',
];
// Apply all the session cookie settings without ini_set() for maximum portability:
session_name($session_cookie_name);
session_set_cookie_params($session_cookie_options); // Since PHP 7.3 only
session_start();
// If the session cookie has been sent by the browser then it might have an expiration
// date to early (because it is set only once at the creation of the session).
// Instead we would like it to expire with our lifetime since the last user's
// action. To do that we have to use setcookie() to resend the cookie in order
// to update/overwrite it to have a new expiration date in the browser.
if (isset($_COOKIE[$session_cookie_name]) && $_COOKIE[$session_cookie_name] == session_id()) {
$cookie_options = $session_cookie_options;
unset($cookie_options['lifetime']); // This one is replaced by expires below.
$cookie_options['expires'] = time() + COOKIE_LIFETIME;
setcookie($session_cookie_name, session_id(), $cookie_options);
}
// Now that HTTP headers have been set, we are allowed to start printing output.
// If the user is already logged and his caddie is empty then fill it with some
// random stuff. It will stay saved until the session expires.
if (isset($_SESSION['session_creation_date']) && !isset($_SESSION['shop_caddie'])) {
$_SESSION['shop_caddie'] = [
'T-shirt' => [
'quantity' => rand(1, 3),
'color' => 'red',
],
'Beer' => [
'quantity' => (['2dl', '3dl', '5dl'])[rand(0, 2)],
]
];
}
// If the session is empty, let's init it with the creation date for the showcase.
if (empty($_SESSION)) {
$_SESSION['session_creation_date'] = date('r');
}
print 'Recieved cookies from the browser = ' . var_export($_COOKIE, true) . "\n\n";
print 'Session data = ' . var_export($_SESSION, true) . "\n\n";
Wondering if someone can provide some clarification on why this is behaving how it is when using samesite in a php session cookie.
example.com has the following :
session_name('Example_Login');
session_set_cookie_params(['lifetime' => 0, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'strict']);
session_start();
test.com has the following form posting to example.com :
<form method="post" action="https://www.example.com/" target="_blank" autocomplete="off">
<input type="hidden" name="username" value="demo_user">
<input type="hidden" name="password" value="demo_password">
<input type="hidden" name="signin" value="signin">
<button type="submit" name="submit">Login</button>
</form>
example.com receives the post and with php I use the $_POST variables sent to validate the login credentials and log the user in. With those values being valid, the user is not logged into example.com though. If, I change the samesite parameter on example.com's session cookie to 'lax' the posted form works as expected.
I did read up on the samesite parameter before adding it and I did not see anything that stuck out to me where it would affect posts/gets. What am I missing here? I don't see how the samesite parameter has ANY affect on what I am doing here. I sent a post from another domain, retrieve the variables, and do some logic with php... what does the samesite parameter for the session cookie have to do with anything here?
UPDATE:
I did some debugging. The post variables are sent and received fine, session is created on example.com and creates lots of $_SESSION vars, etc. I narrowed down the problem to a redirect that happens after the username/password is validated in php. If the user/pass is correct and the account exists I store user information in $_SESSION then I call the following in php :
header("Location: /main.php");
exit();
The redirect happens and upon reaching main.php $_SESSION is empty. All of its variables are gone. I echo'ed it and it shows the following :
Array
(
[user] => Array
(
[session] => 1
)
)
I switch the samesite parameter to 'lax'. Run the exact same debugging and $_SESSION is full of my user information as expected which was put there before the redirect.
I also changed my redirect to absolute as header("Location: https://www.example.com/main.php"); to see if that had an affect, but the problem still remains.
So, my question now is... when using samesite='strict' in my session... why is the session emptied after a redirect to a page on the same domain?
UPDATE 2:
I changed the session save path to another location specifically to debug and see what happens. When it reaches example.com it creates the session file and the values I added into it are there. When it reaches example.com/main.php (the redirect) it creates a brand new session file as seen above. My session settings and start are in their own file which is required first thing on these pages :
session_name('Example_Login');
session_set_cookie_params(['lifetime' => 0, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'strict']);
session_start();
So with samesite='strict' the above creates a new session, but with samesite='lax' it uses the same session file from the previous page. What gives? I am seeing where things are going wrong, but not why it is happening or how to fix it.
UPDATE 3:
Created a very simple test to demonstrate what is happening and why. See answer below. One can argue with me all day justifying why this works how it does, but I think the logic happening here is flawed.
SameSite=Strict means the cookie will not be sent on cross-site requests which includes cross-site POST requests and redirects triggered from the cross-site POST request.
SameSite=Lax is the correct option for your session cookie here. Being able to use Strict here is not better or more secure, it is too restrictive for this use case.
I've boiled this entire thing down to a simple test.
example.com has two pages :
test1.php
<?php
//session name
session_name('Test');
session_set_cookie_params(['lifetime' => 0, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'strict']);
session_save_path($_SERVER['DOCUMENT_ROOT'].'/sessions');
session_start();
//session stuff
$_SESSION['sessiontest'] = 'worked';
//cookie stuff
setcookie('testing', 'worked', ['expires' => 0, 'path' => '/', 'domain' => 'example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'strict']);
//redirect to the other page
header("Location: /test2.php");
exit();
?>
test2.php
<?php
//session name
session_name('Test');
session_set_cookie_params(['lifetime' => 0, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'strict']);
session_save_path($_SERVER['DOCUMENT_ROOT'].'/sessions');
session_start();
echo '<pre>'.print_r($_SESSION,1).'</pre>';
echo '<pre>'.print_r($_COOKIE,1).'</pre>';
?>
test.com
go to test
If you start at test.com the session and cookie (created at example.com/test1.php) are empty at example.com/test2.php. This all has to do with the chaining and logic being used for samesite which in my opinion is flawed in this case. The session and cookie it is preventing are being CREATED on the site this is supposed to protect from the 'outside'. I'm sure someone will have some argument justifying this, but as far as I see it this whole thing was created to mitigate the results of bad coding... and in this case... is preventing legitimate code from working as it should. This example shows I am not 'using' anything from test.com... yet because of the redirect happening when you reach example.com/test1.php it sees the session and cookie created on it as more or less 'bad' when reaching example.com/test2.php because the chain started at test.com.
I have started using Yii2 basic and need to store session information. I already know that in Yii2 basic, this should be done using sessions like
$session = Yii::$app->session;
$session->open();
$_SESSION["a_id"] = $id;
$_SESSION["w_auth"] = "true";
The problem with this is that every time the browser is closed my session expire
Is there anyway to keep session alive or set session destroy so even I close the browser and open it again. It will not ask me again to put my username or password.I need to do this in the YII2 Basic .
session cookies set expire time after 7 days
`
'components' => [
'session' => [
'class' => 'yii\web\Session',
'cookieParams' => ['lifetime' => 7 * 24 *60 * 60]
],
`
You need to use cookies for this.
Cookies are info kept in your browser.
Here is how to do in yii2:
$cookies = Yii::$app->response->cookies;
// add a new cookie to the response to be sent
$cookies->add(new \yii\web\Cookie([
'name' => 'a_id',
'value' => $id,
]));
Add the above cookie when you login and then use it this way in your actions:
$cookies = Yii::$app->response->cookies;
$a_id = $cookies->getValue('a_id');
if($a_id !== null) {
// user is logged in
}
Note: What is kept in the cookie of your browser is not your actual info, but the session id and this is sent when you reopen your browser and restores your session by this id. Your actual info is kept in your session(in the server). This how yii 2 cookies work.
References
What are cookies and sessions, and how do they relate to each other?
Cookies vs. sessions
Cookies arent being set in my first view.
Is this how it should be ? or am I mistaken
Please view the code below for further illustration:
if( !get_cookie('rate') ){
$cookie_rate = array(
'name' => 'rate',
'value' => '10',
'expire' => '100000'
);
set_cookie($cookie_rate);
}
var_dump( get_cookie('rate') ); //returns ( boolean false )
if( !isset($_COOKIE['foo']) ){
$_COOKIE['foo'] = 'bar';
}
var_dump( $_COOKIE['foo'] ); // This yields string 'bar' (length=3) on the first visit
Doing the same thing with php cookies yeilds an array with cookie values.
This problem persists only in the first visit of a page.
This isn't a CodeIgniter vs PHP issue. You just don't understand how cookies work.
Cookies are a header sent by the server which the browser must send back on subsequent requests. $this->input->set_cookie() and set_cookie() send a cookie header.
$_COOKIES on PHP (and thus $this->input->cookie() and therefore the get_cookie() helper) only contain cookies which the browser sent.
Thus when you set a cookie with set_cookie(), you won't be able to get_cookie() until the browser's next request.
Seeing as CI's set_cookie probably utilizes the same mechanism as PHP's setcookie(), this entry from the manual probably applies:
Common pitfalls
Cookies will not become visible until the next loading of a page that the cookie should be visible for. To test if a cookie was successfully set, check for the cookie on a next loading page before the cookie expires. Expire time is set via the expire parameter. A nice way to debug the existence of cookies is by simply calling print_r($_COOKIE);.
I can't see any mention of this behaviour in the CI manual, but it's likely to be the same thing.
If I understood this right youre trying to see the data of the cookie before reloading the page which won't work. Try setting the cookie, reloading the page and then checking the cookie for data.
Also sometimes you need to specify domain and such, perhaps it doesn't apply right now but it might be nice to know later if they suddenly stop working, from CI-documentation:
$cookie = array(
'name' => 'The Cookie Name',
'value' => 'The Value',
'expire' => '86500',
'domain' => '.some-domain.com',
'path' => '/',
'prefix' => 'myprefix_',
'secure' => TRUE
);
$this->input->set_cookie($cookie);
Also, consider using the session class instead of cookies if you don't need anything specifically from cookies since they are more secure. http://codeigniter.com/user_guide/libraries/sessions.html
Thats probably not the problem, in fact thats how cookie works, its set and on next page load it seems to be visible from then.
I have set cookie and set it to expire after sufficient seconds. Still as soon as my session expires the cookie also expires. This is my code :-
if(isset($_POST['KeepMesignedIn'])) {
$this->load->helper('cookie');
$cookie = array(
'name' => 'info',
'value' => $user->Username . '||' . $user->Password,
'expire' => time()+3600*24*30
);
set_cookie($cookie);
}
Can anybody identify the problem?
According to the CodeIgniter documentation, set_cookie expects expires to be the delta seconds that are added to the current time:
The expiration is set in seconds, which will be added to the current time. Do not include the time, but rather only the number of seconds from now that you wish the cookie to be valid. If the expiration is set to zero the cookie will only last as long as the browser is open.
check is this part of code executed in your app or not. You need to debug.