I have a session formed the following way:
function sec_session_start() {
$session_name = 'primary_session';
$secure = false;
$httponly = true;
if (ini_set('session.use_only_cookies', 1) === FALSE) {
header("Location: /error?e=1");
exit();
}
$cookieParams = session_get_cookie_params();
session_set_cookie_params(3600,$cookieParams["path"],$cookieParams["domain"],$secure,$httponly);
session_name($session_name);
session_start();
session_regenerate_id(true);
}
I use this on all my page by adding
sec_session_start(); on my index page, which requires correct files depending on what page I am accessing.
It works perfectly fine with slow navigation.
However, when rapid navigational clicks occur, for some reason it unchecked, and the user is logged out.
How come?
This is the button I press rapidly. NOTE: It also changes the page from www.example.com to www.example.com/users, and then just repeats www.example.com/users until session is broken.
And this is the result after about, 2-3 rapid clicks. Works fine when pressed 1-2 times a second, max.
I have tried not using it as a function, and putting it on the absolutt TOP of the page without success.
The error seems to be session_regenerate(true).
This command generates a new session id. The parameter will delete the old session file if it is set to true. In this code it is set to true, so the session is created an started and then directly closed and deleted.
I think it appears only a few times because the command is called after session_start() was called and the output already started.
Try changing the parameter to false.
For the right use of session_regenerate() look into this question.
You appear to be discarding the old session ID on every page load. This is unnecessary, inefficient, and causes breakage.
If you navigate twice in quick succession what can happen here is:
the first request hits the server and starts executing your PHP, causing session_regenerate_id(true) to destroy the old session
this will cause the response from the PHP script to include a Set-Cookie: sessionid=something header, asking the browser to update its cookie to point to the new session
but the browser hasn't received the response from the script quite yet
you click a second time
the browser discards the existing request. Any Set-Cookie header isn't going to get listened to now
the browser makes a new request to your server. The browser doesn't know anything about a new session, so it includes the old session cookie
your script sees the old session cookie, which points to nothing, so user has to start a new session which isn't logged in
If you have an anti-Cross-Site-Request-Forgery system based on storing a synchroniser token in the session, then regenerating the session ID on every page load will also make any forms you use inoperable when the browser has multiple tabs open on the site at once, or when the user navigates with the Back button.
You should only session_regenerate_id when the authentication associated with a session is changed (primarily, when the user logs in).
Changing session IDs does not prevent session fixation; it is only a mitigation for when session fixation has already occurred through some other means (eg a vulnerable app on a neighbour subdomain injects a cookie into the shared parent domain).
If you didn't change the session ID, then an attacker who had already gained session fixation could get a full session hijack, by giving you a session ID she already generated and knows, and letting you log in using that session, upgrading it to an authenticated session. When you change the session ID on auth boundaries that isn't possible; the worst she can do now is push you into a session in which you are unexpectedly logged in as her. Which isn't ideal, but generally this constitutes a much less damaging attack.
Related
I am using Facebook's PHP SDK for validating users to leave comments and it works quite well. Once, validated, I store the user information in a session variable, but first call session_regenerate_id() and then reload the page. When the page reloads, the old session data is still available, including the Facebook SDK state variable, however, the session variable I added is not available. The following is a snippet of the code:
session_regenerate_id();
$_SESSION[...] = ...;
header('Location: ...');
die();
If I take out the session_regenerate_id() then everything works perfectly. Any ideas what I am doing wrong?
EDIT
If I log session_id() every page load, I see that session_regenerate_id() generates a new id and the session contains everything I expect. However, when the page reload occurs, the session id is the previous session id and not the new one, hence I cannot access the new session variables. Why would this happen?
After a lot of logging and scanning the headers being sent and received, I determined that when the initial session was created, the domain used for the cookie was: .domain.com (without the www). However, session_regenerate_id() was setting the domain for the cookie to: www.domain.com. When the browser made a determination of which to send, it always sent the original one, so the session used was always the old one. Once I manually deleted that cookie, everything worked fine.
To ensure this sort of thing doesn't happen again, I added the following before starting my session:
session_set_cookie_params(0, '/', $_SERVER['SERVER_NAME'], true, true);
What is odd, the .htaccess file enforces www.domain.com for consistency, so I am not sure why the initial cookie's domain was set the way it was.
I have an application where the login and logout works correctly. Once the user logs out, and tries to access a page he needs authentication for, he is redirected to the login screen.
Where my problem lies is. If while I am logged in, if I copy the cookie values and save them on a file. After logout, I alter the cookie and add the same values, I get logged back in into the application as the same user.
On logout I have written a function that loops over all the cookies and deletes them.
My understanding is that cookies are both on the client and also on the server side. So if the cookies are getting deleted, they are getting deleted on both the sides and that the server would not recognize them after they have been cleared, even if the browser sends them back again(apparently that is not the case, i think).
The reason why I am doing this is because this is one of the points raised by our security auditor, and I need to get a way to fix this hole. (At this point doing https is not feasible)
I'd be happy if someone can give me pointers on how I can clear out the cookies on the server side as well, so, when the next time someone hits the server with the same cookie, it does not accept it as a valid cookie.
Edit:
I am using codeigniter sessions and tank_auth as the authentication library. At logout, the library itself calls
$this->ci->session->sess_destroy();
to be extra sure, I tried the following after a few attempts :
session_start();
session_unset();
session_destroy();
session_write_close();
setcookie(session_name(),'',0,'/');
session_regenerate_id(true);
My regular logout works, and if I try to access the page directly it does not open.
But if while I am logged in, I take my cookie, save it somewhere -- log-out successfully and replace the cookie with my older one, I get right back into the session.
Is there a way to stop this behavior -- Where the server side will not entertain a session after it has been destroyed. I also made sure that my server and php are on the same timezone (setting it with date_default_timezone_set).
Cookies are not stored on the server at all. Those are stored in the browser and then sent to the server in the request headers. You can easily find software and plugins for browsers that allow you to create/edit/delete cookies. For that reason you should never store sensitive information in cookies. Essentially what you want to do is store the user data in a session and then store the session name in a cookie. Usually this is done automatically in php when you use the function session_start().
If you are using Codeigniter, the php session functions are wrapped in a CI session library that is auto loaded on each page load. So instead of storing data in $_COOKIE you will want to get/set your data via the userdata method in the session library:
//in your controller
//save session data
$userdata = array(
"isLoggedIn"=>true,
"username"=>$_POST['username']
);
$this->session->set_userdata($userdata);
//get session data later
$isLoggedIn = $this->session->userdata("isLoggedIn");
if(!$isLoggedIn){
//if the user is not logged in, destroy the session and send to the login screen
$this->session->sess_destroy();
redirect("/");
}
Note that the code above is not tested and is only supposed to give you an idea on where to go. If the session methods aren't working for you, you may need to load the library in manually:
//in the __construct method of your controller:
$this->load->library("session");
You can find more information here:
http://ellislab.com/codeigniter/user-guide/libraries/sessions.html
and here:
http://www.php.net/manual/en/book.session.php
Thanks for you answers guys.
This is what I figured, later. I am not sure what was causing this but the sessions were not getting invalidated after trying everything. I moved the sessions on codeigniter to the database. Then the logouts started working correctly, where after logout if the 'stolen'/'saved' cookie was put in the browser again it would Not log the user back in.
So, thats what solved it.
I use Codeigniter (which may be the reason for my issue) and I identify users by a couple of cookies and session variables. When users want to sign up but are detected like already having an account based on their cookie (e.g someone else signed up on their computer), this is what currently happens in the controller after the signup form has been submitted:
if detected user #4 already has an account but still signs up: delete
session and cookies, then create new user-identifying session and
cookie as user #5 and proceed with the DB stuff.
else proceed with the DB stuff as user #4.
The issue: deleting the session and/or cookie only happens at next page load, which means that I sign user 4 up (overwriting the current user 4) but it becomes user 5 (who has nothing in the DB) as soon as the next page loads. The correct behaviour would be to get the deletion instant, so that when I create the new session + cookie it correctly creates the account for user #5.
How can I delete session variables + cookies instantly, not at next page load?
UPDATE:
After some testing this is what happens: cookies don't get deleted before next page load; session variables get deleted instantly but not set before next page load. So 2 different issues to fix so that they both get instantly deleted and set again.
detect user
delete cookies
optionally you could create new session data right here
redirect to some page of your choosing :)
that way it will pick it up correctly (and create new data)
In the end I changed my script to create a new user directly and not rely on cookies or session to identify the user, which means I don't have to worry about when cookies or session variables get deleted.
I have looked everywhere on the web, and the nuances of SESSION variables need some clarification for me.
I have a website that sets a session variable (I assume it also sets a cookie defining what the session id is? Not sure how that works), and all works perfectly. What I've found when roaming(using wifi) by cellphone, that whenever the phone switches wifi networks, the browser seems to be unable to access the session anymore until that window/tab is closed and reopened.
From what I've read the following apply, please clarify for me:
When browser windows are closed, the session cookie is destroyed, thus when I reopen it, a new cookie is created attached to the new session id. Thus allowing the session to work properly again?
If the network is switched, the server creates a new session id, but because the browser window wasn't closed, the old session cookie wasn't destroyed, and the browser tries to manipulate an expired session id (the old one the session cookie contains)? No idea if this is true, I have read a ridiculous number of pages on this and I can't find anything specific. But this is the impression I get. I have seen so many warning about session_regenerate_id() that I am very nervous about using it...
Any help on details about this, or ways to fix it would be extremely helpful. I am at my wit's end...
UPDATE
I am using a mypajamas script to facilitate logins. The sessions are created normally. However, after looking through the code, I found a place where a session variable is set for the $_SERVER['REMOTE_ADDR'] and $_SERVER['HTTP_USER_AGENT'] values. They are then cross referenced to ensure it's the same user on the same browser on the same IP. It was done to prevent multi-source attacks (make a hijack unlikely). The problem is when using mobile browsers, or dynamic IP's the IP can definitely change, and authentication fails.
I can't believe I couldn't figure this out, I spent hours looking for issues regarding session id's changing.
Hopefully it helps someone with a similar issue in the future. And a sincere appreciation to all who read this post, your guidance in the comments definitely helped me troubleshoot this.
EDITED CODE
class.mypajamas.php
From:
function check_ipau() { // user visits again... but is it really him? check values set above (by get_session())
if( $_SESSION['auth'.$this->_unique_id]['ip'] == $_SERVER['REMOTE_ADDR']
&& $_SESSION['auth'.$this->_unique_id]['ua'] == $_SERVER['HTTP_USER_AGENT']) {
// session data is correct -> user did not "change" ip-address or user agent (aka; hijack is unlikely)
return true;
}
else {
return false;
}
}
To:
function check_ipau() { // user visits again... but is it really him? check values set above (by get_session())
/*if( $_SESSION['auth'.$this->_unique_id]['ip'] == $_SERVER['REMOTE_ADDR']
&& $_SESSION['auth'.$this->_unique_id]['ua'] == $_SERVER['HTTP_USER_AGENT']) {
// session data is correct -> user did not "change" ip-address or user agent (aka; hijack is unlikely)
*/
return true;
/*
}
else {
return false;
}*/
}
First of all, session variables are actually cookies.
1) Session cookies have a lifetime. If your code isn't specifying the lifetime, then the value defaults to whatever is in your php configuration. Do a phpinfo(); and look for session.cookie_lifetime, which is a value in seconds. A value of 0 means the cookie expires when the browser window is closed.
2) If your code is using boilerplate PHP Session handling, then it's unlikely that the issue is related to the user's ip address changing. PHP sessions do not store client ips, and as long as the application you're connecting to has the same domain name/public ip across both networks, then you should be fine. (see PHP Session Cookies fail with users changing IP)
It's possible that there may be some added Session handling that stores the client's IP, but that would have to be custom coded and not based on any built-in functionality.
(based on your edit, this was, in fact the case.)
If you're accessing the same url on network A and on network B, then there is no reason why the session will change / be affected by the network switch. If you have to close the browser window down, and find that your session data is gone, then the problem simply lies in setting the lifetime of the session cookie to be a value other than 0. Place the following line before session_start():
session_set_cookie_params(X);
Where X is a value in seconds after which the cookie will expire. Keep in mind that if you call the aforementioned code before every instance of session_start(); then the cookie will effectively never expire since every single page will be resetting that counter ahead by one hour.
Sessions are server-side only. Unfortunately, if you are coming from a different network address the session will not be valid.
A good practice if you want to retain session values is to create your own cookies, so that you know someone was once authenticated/had a valid session. You then first check if the session values are set, if not check for that cookie. If that cookie exists and has a good value, you can re-set the session value to that of the cookie (or just set the session as you would upon authentication). If there is no session and no cookie then the session is presumed to be non-existent.
Caching the HTML output of non-important pages (like blog posts) really helps to speed up a site by skipping the loading of the entire system (and rendering time) and just spiting out a pre-made copy of the page. This would be one way you can keep large waves of users to your site index or whatever from eating resources.
However, one of the problems of caching pages is that in order to check wither or not you can show the page - you have to load the entire system (and the user lib) to check if the user is logged in. (Wordpress, CodeIgniter, Drupal, etc..)
Then you can determine if it is safe to show a cached version of the page or if you should re-render the page for the logged in user. And vis-versa; a page rendered for a logged-in user shouldn't be shown to a guest!
Anyway, I finally had an idea that I could just use if(empty($_COOKIE)) to test for a user session since I never use the URL to transfer the session ID. Then I remembered that since the session library is loaded on each page that probably won't work since it will create a cookie when session_start() is called.
Does anyone have any ideas about how to test for a user session without loading your database connection -> for your session library -> for your user library?
I just recently learned this, so I hope I have everything straight.
In order to access saved session variables, you have to have a session started. In the project I'm working on, the 'user login' and 'create new user' pages all start out with session_start(). I also tried testing if the cookie was set, which didn't work too well. If the user is logged in or not, it doesn't matter, because there will be a cookie for both guests and logged in users. If you have a session then you'll have a cookie (provided that's how you have php setup, which sounds like you do). So instead of testing the cookie, I test to see if a session variable is set. This variable will only be set once the user successfully logs in.
if (loginSuccess()) { $_SESSION['login']="true"; }
After this, I can now test to see if that session variable is set, and display content appropriately. My user can navigate away from those pages, come back, and it will still remember that session variable.
I'm sure there are better, secure ways of doing this though.
You could set some kind of session variable once the user logs in, i.e.
//user has successfully logged in
$_SESSION['logged_in'] = true;
Then, somewhere very early in your page load (before any libraries have loaded) you could do:
#session_start();
if(!(isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true))
{
//not a logged in user, show the cache!
load_cache(); //or however this works
exit();
}
On a logout, you'd either do session_destroy() or $_SESSION['logged_in'] = false;
Yes, as I stated the current method involves loading the system, database (assuming your sessions use the database), and finally starting the session which is a waste if you are only going to show cached content.
Perhaps you could set a cookie called "logged_out" (or "logged_in") in addition to the session cookie. Then you could test for "logged_out" cookie on each page load without the need to load the session and database?
Actually, since you would have bots and other non-cookie user agents you it would be better to create a "logged_in" cookie on login and then everywhere else check for a false/non-existent cookie as a sign for a guest.