I would like to dynamically set the session expiration time in Codeigniter. I'm autoload the session class. I have a view that contains checkbox for users to click (remember me). Right now if they click the check box or not the expiration time stays the same :/
// Config.php
$config['sess_expiration'] = 7200;
// Controller
if ($this->input->post('remember_me') == 'TRUE')
{
$this->session->remember_me();
}
$newdata = array(
'failed_login' => 0,
'user_name' => $this->input->post('user_name'),
'logged_in' => TRUE
);
$this->session->set_userdata($newdata);
// MY_Session.php
class MY_Session extends CI_Session {
function remember_me()
{
$this->sess_expiration = 172800;
}
}
If you need to implement "Remember me" feature - then you've started it in a wrong way.
You need to create one more database table with fields user_id | token.
Then, after user has been logged in (with "remember me" checkbox checked on) - generate random token and insert a new row with current user_id and that token. Also - set remember cookie with the same token value.
Now, if user enters your site, not authenticated and has some token - you can always find that token and authenticate user (each token is unique and strognly related to specific user_id).
Here is my solution I've been playing with.
You can edit the function inside domain.com/system/libraries/Session.php
function _set_cookie($cookie_data = NULL)
Comment out
// $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
Then Add
if(isset($this->userdata['rememberme'])){
// If they want to stay connected
if( $this->userdata['rememberme'] == 1) {
$expire = 0;
} else {
$expire = time() + $this->sess_expiration;
}
} else {
$expire = time() + $this->sess_expiration;
}
Please let me know if this helps in anyway or modify my code to help me out. Thank you.
$data = array(
'username' => $this->input->post('username'),
'ADMIN_is_logged_in' => true
);
$this->session->sess_expiration = '14400';// expires in 4 hours
$this->session->set_userdata($data);// set session
Related
I am currently working on a website and I am having hard time understand how can I make a secure and flexible system where I can manage user's logged in state.
I am aware of $_SESSION and $_COOKIE and I understand how they work, however, I can't find a way to use them properly.
Here is a piece of code that I built for keeping track of whether the user is signed in or not.
state_handler.php
include_once(__DIR__ . '/../setup.php');
include_once(COOKIE_UTIL_PATH);
include_once(SESSION_UTIL_PATH);
if (is_session_set() === FALSE) {
if (is_cookie_set() === TRUE) {
include_once(DB_PATH);
$data = get_cookie_data();
$user = select_user_by_username($data['username']);
if (!$user || strcmp($data['password'], $user['password']) !== 0) {
destroy_cookie();
header('Location: /index.php');
exit();
}
session_start();
set_session_params($user);
$new_data = array(
'username' => $user['username'],
'password' => $user['password']
);
create_cookie($new_data);
} else if (strpos($_SERVER['REQUEST_URI'], '/src/signin/signin.php') === FALSE) {
header('Location: /src/signin/signin.php');
exit();
}
} else if (strpos($_SERVER['REQUEST_URI'], '/src/signin/signin.php') !== FALSE) {
header('Location: /index.php');
exit();
}
cookie_util.php
function create_cookie(array $data): void
{
setcookie(
'user_information',
json_encode($data),
[
'expires' => time() + 1296000,
'path' => '/',
'httponly' => true,
'samesite' => 'Strict'
]
);
}
function is_cookie_set(): bool
{
return isset($_COOKIE['user_information']);
}
function get_cookie_data(): array
{
return json_decode($_COOKIE['user_information'], true);
}
function destroy_cookie(): void
{
setcookie(
'user_information',
'',
[
'expires' => time() - 1296000,
'path' => '/',
'httponly' => true,
'samesite' => 'Strict'
]
);
setcookie(
'PHPSESSID',
'',
time() - 1296000,
'/'
);
}
session_util.php
function set_session_params(array $params): void
{
if (!isset($_SESSION)) {
session_start();
}
$_SESSION['id'] = $params['id'];
$_SESSION['username'] = $params['username'];
$_SESSION['password'] = $params['password'];
$_SESSION['name'] = $params['name'];
$_SESSION['surname'] = $params['surname'];
$_SESSION['group_id'] = $params['group_id'];
$_SESSION['team_id'] = $params['team_id'];
$_SESSION['city_id'] = $params['city_id'];
}
function is_session_set(): bool
{
if (!isset($_SESSION)) {
session_start();
}
return isset($_SESSION['id']);
}
function destroy_session(): void
{
if (!isset($_SESSION)) {
session_start();
}
session_unset();
session_destroy();
}
Once user signs in, he is sent a cookie with his Username and Password(hashed with MD5).
This cookie will live for 30 days. If the session expires, I then extract data from the cookie and compare it with DB data. If it works, then I simply start another session and refresh cookie's lifetime.
However, after giving it a bit more thought, this solution is quite problematic.
I am storing user's credentials (doesn't matter hashed or not) in cookies, which I think is a safety hazard.
I have no way of terminating user's logged in state as an admin. Even if I delete him from DB the user will stay active until the session expires, which is not something I want.
What would be the correct way of solving this? I've been doing this for a little more than a month and don't have lots of experience with website development, so please, help me out here.
One of the most important rules in web programming is never trust any information sent by the client (browser).
Although you have code to set the cookie, the value you see on the next request is not under your control: there is nothing to stop the user pressing F12 in their browser, and setting the cookie themselves.
This is why the concept of sessions was invented: the cookie you send to the browser is just a random ID, and the browser can neither read nor write the actual data you've associated with that ID.
So:
Don't set a custom cookie, just use the session; PHP will manage the cookie for you
You don't need to store the user's password, hashed or otherwise, to know that they're logged in
Even once a user's logged in, you can check in the database as often as you like to see if they've been disabled; this might be every request, every 5 minutes, or every time they do something particularly dangerous
Some very rough code, which you should read as an example not just copy and paste might look like this:
session_start();
if ( isset($_POST['username']) && isset($_POST['password']) ) {
$user = select_user_by_username($_POST['username']);
if ( password_verify($_POST['password'], $user['password_hash']) ) {
$_SESSION['login_successful'] = true;
$_SESSION['login_expires_at'] = strtotime('+30 minutes');
$_SESSION['user_id'] = $user['id'];
}
}
if (
! $_SESSION['login_successful']
|| $_SESSION['login_expires_at'] < time()
|| user_is_disabled($_SESSION['user_id'])
) {
redirect_to_login_page();
exit;
}
// Extend the login to be 30 minutes from last activity, not 30 minutes from login
$_SESSION['login_expires_at'] = strtotime('+30 minutes');
I am using PhpMyAdmin and a custom single-sign on (SSO) script for direct login into the interface. The SSO script is called by PHP, given a unique login token by my own system. This script looks up the unique id in my system in order to retrieve MySQL username and password and returns this back to PhpMyAdmin.
This is working so far, but my next goal is automatic logout after a certain amount of inactivity. Without SSO, deleting my browser cookies and clicking any link, I get to the login page with the message »Your session has expired. Please log in again.«. However, I'm not able to reproduce this behavior from within my SSO script.
This is my SSO script:
<?php
/**
* Session timeout in seconds.
*/
define('SESSION_TIMEOUT', 60);
/**
* #return array|null Returns an array with login credentials or null for no login.
*/
function get_login_credentials() {
parse_str($_SERVER['QUERY_STRING'], $query);
/* check for session activity (timeout) */
if (isset($_SESSION['ssoLastActivity']) && (time() - $_SESSION['ssoLastActivity']) > SESSION_TIMEOUT) {
$sessionExpired = true;
} else {
$sessionExpired = false;
}
if (isset($query['old_usr'])) {
/* logout and back to index page */
unset($_SESSION['ssoLastActivity']);
unset($_SESSION['ssoUser']);
unset($_SESSION['ssoPassword']);
header('Location: index.php');
exit;
}
if ($sessionExpired) {
unset($_SESSION['ssoLastActivity']);
unset($_SESSION['ssoUser']);
unset($_SESSION['ssoPassword']);
/******** POINT OF QUESTION ********/
/* I'm trying to give the same response as if the cookies were deleted.
I land on the login page as desired, however I'm missing the session
timeout message. */
header('Content-Type: application/json');
echo json_encode(['redirect_flag' => '1', 'success' => false, 'error' => '']);
exit;
/***********************************/
}
/* update session activity timestamp */
$_SESSION['ssoLastActivity'] = time();
if (!empty($_SESSION['ssoUser']) && !empty($_SESSION['ssoPassword'])) {
/* already logged in */
return [
$_SESSION['ssoUser'],
$_SESSION['ssoPassword'],
];
}
/* retrieve MySQL login credentials here and store them in $user and $password */
/* $user = ...; $password = ...; */
return [
$user,
$password,
];
}
Has anybody a solution for logout via my SSO script, that leads me to the login page with the message, that the session is expired?
UPDATE:
The issue seems to be connected to my PhpMyAdmin server configuration (/etc/phpMyAdmin/servers.ini.php in my case):
<?php
$cfg['Servers'] = array(
1 => array('auth_type' => 'signon', ..., 'SignonScript' => '/usr/share/phpMyAdmin/sso.php', 'SignonURL' => 'index.php?server=1'),
2 => array('auth_type' => 'cookie', ...)
);
I inspected the network request after my session timeout, and it turns out, that there's actually a request with ?session_expired=1 (which triggers the session timeout message) sent to server 1; because this script is returning null (no login), it redirects to the SignonURL index.php?server=1, omitting the extra session_expired query param.
I could extend this url by &session_expired=1, however this would also trigger the message on regular logout.
I'm open for any ideas to improve the behavior.
I can't seem to get cookies set from my controller.
code:
/**
* #Route("", name="wx_exchange_default_index")
* #Template("WXExchangeBundle:Default:index.html.twig")
* #Method({"GET"})
*/
public function indexAction(Request $request)
{
$returnArray['parents'] = self::getCategories();
$cookieLocationSession = $request->cookies->get('locationidsession', 0);
$cookieLocation = $request->cookies->get('locationid', 0);
$securityContext = $this->container->get('security.context');
$hashids = $this->get("wxexchange_hashids_service");
if ($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED'))
{
$user = $securityContext->getToken()->getUser();
$returnArray['user'] = $user;
$location = $user->getTblProfile()->getTblLocation();
if(!$cookieLocation || $cookieLocation != $hashids->encrypt($location->getId()))
{
$cookieLocation = $hashids->encrypt($location->getId());
//TODO: cookies are not being set figure out whys
$response = new Response();
$response->headers->setCookie(new Cookie("locationid", $cookieLocation, 365, '/', null, false, false));
$response->sendHeaders();
}
}
if (!$cookieLocation && !$cookieLocationSession)
{
return $returnArray;
}
if ($cookieLocationSession)
{
$cookieLocation = $cookieLocationSession;
}
if (!isset($location) || $cookieLocationSession)
{
$locationService = $this->get("wxexchange_location_service");
$locationId = $hashids->decrypt($cookieLocation);
if(count($locationId) >= 1)
$location = $locationService->getLocationById($locationId[0]);
}
if(isset($location))
return $this->redirect($this->generateUrl('wx_exchange_location', array('slug' => $location->getSlug(), 'locationid' => $cookieLocation)));
return $returnArray;
}
Do I have to return the response? If I do how do I keep processing (I have a redirect further down in my code)?
Edit: Interestingly enough if the same cookie is already set (via JQuery) running the code above deletes it, but it won't set it.
Edit: Action code posted.
The cookie object has httpOnly set to true by default, http://api.symfony.com/2.0/Symfony/Component/HttpFoundation/Cookie.html
This means that the browser should not make the cookie visible to client-side scripts. If you need to see the cookie in your scripts you can pass the 7th parameter as false when you create the cookie.
$response->headers->setCookie(new Cookie('foo', 'bar',time() + 60, '/', null, false, false));
If you just need to view the cookie for debugging purposes you can use Chrome Dev tools. They are available under the 'Resources' tab.
Edit : Try $response->sendHeaders();
Oops, Sorry everyone.
It's my error.
In my javascript I'm using a Jquery cookie plugin and when you set a new cookie you tell it the number of days before expiry:
$.cookie('locationid', value, { expires: 365, path: '/' });
Unfortunately I used a part of this syntax in my controller:
$cookie = new Cookie("locationid", $cookieLocation, 365, '/', null, false, false);
The problem is the third parameter is supposed to be a DateTime so while I thought I was telling it to expire in 365 days I probably created a cookie that expired almost instantly after creation.
Thanks for all the answers and time spent on your part, this is why I love SO.
i use Zend_Auth for one of my Projects, but so far haven't figured out how to set the Lifetime for the Session, or how to extend it (lets say it should run 5 minutes and should reset to that when the user makes an action), here is my Initialization code:
$authAdapter = new Zend_Auth_Adapter_DbTable($this->_model->pdo);
$authAdapter->setTableName('normal_folks')
->setIdentityColumn('username')
->setCredentialColumn('password');
$post = $this->_request->getPost();
$authAdapter->setIdentity($post['username'])
->setCredential($post['password']);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if($result->isValid())
{
$userInfo = $authAdapter->getResultRowObject(null, 'password');
$authStorage = $auth->getStorage();
$authStorage->write($userInfo);
if(strlen($post['refferer']) > 1){
header("Location: ".$post['refferer']);
}elseif(strlen($this->_request->getParam('ref_action')) > 1){
Zend_Controller_Action::_forward($this->_request->getParam('ref_action'),"admin",null,null);
}else{
Zend_Controller_Action::_forward("index","admin",null,null);
}
}
Ant this how i check if the user is logged in:
if(Zend_Auth::getInstance()->hasIdentity()){
echo "Woho!";
}else{
die("invalid-identity");
}
Its probably right there in front of me but I just can't figure it out, help? Please? Pretty Please? :D
Authentication state is stored in the registered Auth Storage. By default this is Zend_Session. You can set an expiration time to the Zend_Auth namespace, e.g.
$namespace = new Zend_Session_Namespace('Zend_Auth');
$namespace->setExpirationSeconds(300);
You can also globally configure Zend_Session via
Zend_Session::setOptions(array(
'cookie_lifetime' => 300,
'gc_maxlifetime' => 300));
If you are using different namespace for zend_auth session you can do it like this:
$auth = Zend_Auth::getInstance ();
$auth->setStorage ( new Zend_Auth_Storage_Session ( 'user' ) );
$namespace = new Zend_Session_Namespace('user');
$namespace->setExpirationSeconds(7200); // 2 hours
Could anybody brief about user_token functionality in Auth module? What is a use and how this incorporates in Auth module?
It is used when a user checks the 'Remember me' box on your site. A token is generated for the user and stored in the user_tokens table.
If you look at the Kohana_Auth_ORM class in the _login function, you can see how it is created:
if ($remember === TRUE)
{
// Create a new autologin token
$token = ORM::factory('user_token');
// Set token data
$token->user_id = $user->id;
$token->expires = time() + $this->config['lifetime'];
$token->save();
// Set the autologin cookie
cookie::set('authautologin', $token->token, $this->config['lifetime']);
}
It is used by the auto_login() function also in the Kohana_Auth_ORM class:
/**
* Logs a user in, based on the authautologin cookie.
*
* #return boolean
*/
public function auto_login()
{
if ($token = cookie::get('authautologin'))
{
// Load the token and user
$token = ORM::factory('user_token', array('token' => $token));
if ($token->loaded() AND $token->user->loaded())
{
if ($token->user_agent === sha1(Request::$user_agent))
{
// Save the token to create a new unique token
$token->save();
// Set the new token
cookie::set('authautologin', $token->token, $token->expires - time());
// Complete the login with the found data
$this->complete_login($token->user);
// Automatic login was successful
return TRUE;
}
// Token is invalid
$token->delete();
}
}
return FALSE;
}
It is up to you to correctly use this capability within your authorization controller. I'm relatively new to Kohana, but I perform a simple check to redirect a user if they go to the login form and are already logged in or can automatically login:
if (Auth::instance()->logged_in() || Auth::instance()->auto_login())
Request::instance()->redirect('auth/');
The code for the Auth module isn't too difficult to understand. If you're new to Kohana, it's a good starting point to see how the ORM module works.