Making a login session cookie id not theftable by regenerating itself repeatedly - security

To counteract login session cookie theft, sniffing etc i've been thinking about this scheme.
i already read http://jaspan.com/improved_persistent_login_cookie_best_practice and what i wish to do is something less-complicated, maybe performance-wise faster too and something that blends well with both remember me functionality and normal session'ed login.
when registration is successful add session id to user(:uid) table and generate session cookie id for example d6c89ddba79b4f68be07bd874c5ff566 and store it in user browser.
When user visits another,the same page,refreshes it; another id will be generated and the current id in the user(:uid) table, the cookie in the user browser will be updated with the new one making the old one useless || invalid.
If an observer tries to steal the cookie, be it in unprotected connection, for example non-https website it will be rendered useless at user's next visit or refresh.
But... i'm sure there's a flaw somewhere in this logic. can the observer create a sort of time warping technique or do something locally to annihilate the benefits of such a scheme?
One concern comes to mind now.. i think it will be a possible situation that an observer steals the cookie when the user is afk or idle, then the observer refreshes the current page with that cookie or visits another one. the problem is that now the user is logged out while the observer has full grip of his account and can therefore change password if it's not a system where password change is confirmed through email or inserting current password before.
is that right? in that case, how could this scheme be enforced without bringing possibly extra complexity?

Yes, this is a good way to prevent session sharing:
the server will generate a new Session ID to store in the cookie every n number of requests. The Set-Cookie header will only be sent once, so if there are two browsers logged into the same session, one of them will be using the old, invalid session
is that right? in that case, how could this scheme be enforced without bringing possibly extra complexity?
As you say, you can get the user to confirm their password whenever a high security function is invoked such as password change. You could also get the system to confirm their password to continue the session in the case that session sharing is detected (i.e. some requests have been logged using the old session ID).

Related

If I don't send session ID by url do I need to cycle my session id on login

I've heard that my site can get attacked by hackers who:
Go to my site to start a session.
Somehow get a client to go to my site with the same session ID
The client logs in
When the attacker comes back to my site with the session id he has full access to that clients account.
I can see this being done if the session ID is passed by url and it makes sense to cycle it (session_regenerate_id) but is this needed if I just use session_start and I don't put the session id in url at any point in time?
There are session fixation attacks other than session-ID-in-URL. In particular, browser controls over cross-domain cookies are weak.
If an attacker has control over foo.example.com, for example by means of an XSS hole in an application running there, they can write a session ID cookie with parameter domain=example.com, which will then be passed to your application running at bar.example.com and hey presto session fixation.
As a developer you often don't have any control over what other vulnerable applications might be running in neighbour domains, so it is best to assume cookie injection can happen and recycle sessions on a princpal change.
Well, if the session ID is only transferd by a cookie (is not in the URL and you do not accept one in the URL) then it is not that important to protect against session fixation attacks by recycling the session ID.
However it is still good practice, as this could also help against a session cookie which was laying around longer time (and potentially be placed by a former user). So with most security practices it is the same here: just do it, even if you cant find a way it might get exploited.

What are standard security features for protecting user session in web application?

I'm improving security of user sessions in my web application, but I got into trouble. Some of items from my list of security improvements causes user to relogin from time to time. Here's my list, which of then are widely accepted and used? Which are used by you?
checking users ip address (it changes for mobile internet connection) edit: there's no reasonable way to gain access to user ip
checking HTTP_USER_AGENT(some browsers like IE and mobile devices browsers changes it from time to time during single session)
allowing only one session per one user id (when user log in from two locations, he's signed out in the first one)
regenerating session id after each request
Are there any other? My application doesn't process too sensitive data, but should be protected at certain level.
checking HTTP_USER_AGENT(some browsers like IE and mobile devices browsers changes it from time to time during single session)
Given that the HTTP_USER_AGENT is controlled by an attacker, you can't trust it, but you can use it as a signal to detect when a user is legitimately logged in and an impersonator is logged in. The IP address is a similar signal. You may want to look at such signals and choose to weight them so that you get a tolerable level of false negatives.
allowing only one session per one user id
Depending on what you mean by it, it may not be strictly necessary, but if you are going to do
regenerating session id after each request
then you do need to invalidate the old ID when you assign the new to avoid the class 1 session fixation vulns : "A web application authenticates a user without first invalidating the existing session ID, thereby continuing to use the session ID already associated with the user."
When generating IDs, make sure you don't allow untrusted inputs to limit the session IDs that you might generate and make sure your session IDs aren't predictable. Otherwise you are susceptible to the type 2 session fixation attacks : "An attacker is able to force a known session ID on a user so that, once the user authenticates, the attacker has access to the authenticated session."
See https://www.owasp.org/index.php/Session_Management for other security tips on dealing with sessions.

HTTP Remember me authentication

I'm trying to write a simple HTTP remember me authentication system for users.
My users could be represented as such
{
"email" : "foo#bar.com",
"password" : "8EC41F4334C1B9615F930270A4F8BBC2F5A2FCD3" // sha1 hash of password
}
So my idea is that I need to create a cookie, with indefinite (really long) expiration time, that will hold some type of information to enable me to fetch the user from the database, therefore logging the user in.
My first idea was to just simply store the email:password string as a cookie. I thought this would be good since nobody else can really generate that type of information other than the user itself, and I could retrieve the user quite easily by simply comparing the username and password based on what's in the database.
However then I thought this wasn't really good. It turns the password digest into, effectively, a second password that's stored in the clear and passed over the wire in every request.
So then I thought maybe I could generate a signature each time the user logs in, which would basically be a random hash that is stored directly in the user object in the database.
The user logs in, it generates this signature that is stored, and the cookie holds this signature. Whenever you access the website, the site checks which user has that particular signature and logs the user in. Logging out will effectively erase the cookie, and new logins will generate a new random signature.
Does this approach take into account any other vulnerabilities?
I know, I should probably use a library already made for this, but this is just simply an exercise in web-security.
This is essentially what most sites do when you log in. Yes, the cookie should hold a unique identifier for the user's "session". The cookie should be essentially random. Up to you whether to make it persistent across browser sessions.
Along with the cookie in your authentication DB, also store a timestamp of when the entry was created. Cookies older than N seconds should be considered invalid (set N to your taste). You can reset the timestamp each time the cookie is used so that idle sessions time out.
Note that the same user may want to have multiple sessions (do you ever log in to your Email account from both home and work?), so the concept here really is "session", not user.
Vulnerability point-of-view both are same! Cookie stealing and related mechanisms however browsers are smart enough now so you shouldn't worry about that.
Second approach is good in terms of privacy as well since it does not includes email address in the cookie. And it seems much more similar to like storing the sessionID which in your case you are generating a random hash and storing it in DB.
But i think it would be more wiser to use the first approach; you can add another layer to the digest and encrypt it with your some algo or private key; to be on safer side.

Is it safe to store user object in a cookie?

I have a user object which contains information about the user (username, ip, country, name, email... but NOT password).
Should I store just the username in the cookie and then retrieve all info from DB upon loading the page, or just store the entire User object in the cookie?
You can't trust any information stored in a cookie, as the user can manipulate it at his/her leisure.
I suggest using a PHP session to store the object. That way, the end user only has a session ID stored in a cookie, with the real data on your server.
The session will eventually time out, though... forcing the user to log in again.
Edit: Whoops, I should point out that sessions are really easy to use. Just do the following:
session_start(); // This MUST be at the very top of every page that accesses the session
// Store something in the session with the key 'something'
$_SESSION['something'] = "Hi, I'm a session!";
// Retrieve 'something' from the session
$myString = $_SESSION['something'];
The standard rule of 'never trust posted data' applies to cookies too. I suggest storing just the user ID as well as a hash of the ID and some secret known only to the server.
For that case, I'd say store the user-id in the cookie and that's it. Then, upon first load of the page you load everything you need from the database and go on using a session as long as the user stays on your page.
To test if the page is loaded the first time, I just set a bool in the session if it has been loaded. If the bool doesn't exist, your user loads it initially.
There are probably better ways of doing this, but it works nice and easy. :)
Only store a session id! Never meaningful data such as user id. Imagine that you have a site with 10,000 users. Chances are that you have at least one user called superman and batman - if yous tore a username in a cookie to access your session information - it is potentially feasible for me to manipulate that cookie to change stored info from my username to batman and gain access to batman's account if his session is still alive. If you store some sort of randomly generated session id - it's pretty much impossible for me to figure out a session number that would work for another user to hijack that session.
You can trust information in the cookie if you use something like Hmac. Users could still see the data, but you would know if they had tampered with it (for example, changing their username to someone's else's in an attempt to see another user's data). If you don't want them to see the data, you could also symettrically encrypt the data you're sending. Obviously there's a CPU overhead to all of this, and a bandwidth overhead the more stuff you cram in there, but it's entirely legitimate to do what you're asking.
You can't assume the username being passed from a cookie is the actual username you wrote to the cookie. That is why they suggested using the sessionID. Using the sessionID you can go get the username and like he said is only good for 20 minutes or whatever you set your session timeout to be. SessionID doesn't reveal any private data. I had your same thought before I found this post though.

What is the best way to prevent session hijacking?

Specifically this is regarding when using a client session cookie to identify a session on the server.
Is the best answer to use SSL/HTTPS encryption for the entire web site, and you have the best guarantee that no man in the middle attacks will be able to sniff an existing client session cookie?
And perhaps second best to use some sort of encryption on the session value itself that is stored in your session cookie?
If a malicious user has physical access to a machine, they can still look at the filesystem to retrieve a valid session cookie and use that to hijack a session?
Encrypting the session value will have zero effect. The session cookie is already an arbitrary value, encrypting it will just generate another arbitrary value that can be sniffed.
The only real solution is HTTPS. If you don't want to do SSL on your whole site (maybe you have performance concerns), you might be able to get away with only SSL protecting the sensitive areas. To do that, first make sure your login page is HTTPS. When a user logs in, set a secure cookie (meaning the browser will only transmit it over an SSL link) in addition to the regular session cookie. Then, when a user visits one of your "sensitive" areas, redirect them to HTTPS, and check for the presence of that secure cookie. A real user will have it, a session hijacker will not.
EDIT: This answer was originally written in 2008. It's 2016 now, and there's no reason not to have SSL across your entire site. No more plaintext HTTP!
The SSL only helps with sniffing attacks. If an attacker has access to your machine I will assume they can copy your secure cookie too.
At the very least, make sure old cookies lose their value after a while. Even a successful hijaking attack will be thwarted when the cookie stops working. If the user has a cookie from a session that logged in more than a month ago, make them reenter their password. Make sure that whenever a user clicks on your site's "log out" link, that the old session UUID can never be used again.
I'm not sure if this idea will work but here goes: Add a serial number into your session cookie, maybe a string like this:
SessionUUID, Serial Num, Current Date/Time
Encrypt this string and use it as your session cookie. Regularly change the serial num - maybe when the cookie is 5 minutes old and then reissue the cookie. You could even reissue it on every page view if you wanted to. On the server side, keep a record of the last serial num you've issued for that session. If someone ever sends a cookie with the wrong serial number it means that an attacker may be using a cookie they intercepted earlier so invalidate the session UUID and ask the user to reenter their password and then reissue a new cookie.
Remember that your user may have more than one computer so they may have more than one active session. Don't do something that forces them to log in again every time they switch between computers.
// Collect this information on every request
$aip = $_SERVER['REMOTE_ADDR'];
$bip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$agent = $_SERVER['HTTP_USER_AGENT'];
session_start();
// Do this each time the user successfully logs in.
$_SESSION['ident'] = hash("sha256", $aip . $bip . $agent);
// Do this every time the client makes a request to the server, after authenticating
$ident = hash("sha256", $aip . $bip . $agent);
if ($ident != $_SESSION['ident'])
{
end_session();
header("Location: login.php");
// add some fancy pants GET/POST var headers for login.php, that lets you
// know in the login page to notify the user of why they're being challenged
// for login again, etc.
}
What this does is capture 'contextual' information about the user's session, pieces of information which should not change during the life of a single session. A user isn't going to be at a computer in the US and in China at the same time, right? So if the IP address changes suddenly within the same session that strongly implies a session hijacking attempt, so you secure the session by ending the session and forcing the user to re-authenticate. This thwarts the hack attempt, the attacker is also forced to login instead of gaining access to the session. Notify the user of the attempt (ajax it up a bit), and vola, Slightly annoyed+informed user and their session/information is protected.
We throw in User Agent and X-FORWARDED-FOR to do our best to capture uniqueness of a session for systems behind proxies/networks. You may be able to use more information then that, feel free to be creative.
It's not 100%, but it's pretty damn effective.
There's more you can do to protect sessions, expire them, when a user leaves a website and comes back force them to login again maybe. You can detect a user leaving and coming back by capturing a blank HTTP_REFERER (domain was typed in the URL bar), or check if the value in the HTTP_REFERER equals your domain or not (the user clicked an external/crafted link to get to your site).
Expire sessions, don't let them remain valid indefinitely.
Don't rely on cookies, they can be stolen, it's one of the vectors of attack for session hijacking.
Have you considered reading a book on PHP security? Highly recommended.
I have had much success with the following method for non SSL certified sites.
Dis-allow multiple sessions under the same account, making sure you aren't checking this solely by IP address. Rather check by token generated upon login which is stored with the users session in the database, as well as IP address, HTTP_USER_AGENT and so forth
Using Relation based hyperlinks
Generates a link ( eg. http://example.com/secure.php?token=2349df98sdf98a9asdf8fas98df8 )
The link is appended with a x-BYTE ( preferred size ) random salted MD5 string, upon page redirection the randomly generated token corresponds to a requested page.
Upon reload, several checks are done.
Originating IP Address
HTTP_USER_AGENT
Session Token
you get the point.
Short Life-span session authentication cookie.
as posted above, a cookie containing a secure string, which is one of the direct references to the sessions validity is a good idea. Make it expire every x Minutes, reissuing that token, and re-syncing the session with the new Data. If any mis-matches in the data, either log the user out, or having them re-authenticate their session.
I am in no means an expert on the subject, I'v had a bit of experience in this particular topic, hope some of this helps anyone out there.
There is no way to prevent session hijaking 100%, but with some approach can we reduce the time for an attacker to hijaking the session.
Method to prevent session hijaking:
1 - always use session with ssl certificate;
2 - send session cookie only with httponly set to true(prevent javascript to access session cookie)
2 - use session regenerate id at login and logout(note: do not use session regenerate at each request because if you have consecutive ajax request then you have a chance to create multiple session.)
3 - set a session timeout
4 - store browser user agent in a $_SESSION variable an compare with $_SERVER['HTTP_USER_AGENT'] at each request
5 - set a token cookie ,and set expiration time of that cookie to 0(until the browser is closed).
Regenerate the cookie value for each request.(For ajax request do not regenerate token cookie).
EX:
//set a token cookie if one not exist
if(!isset($_COOKIE['user_token'])){
//generate a random string for cookie value
$cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));
//set a session variable with that random string
$_SESSION['user_token'] = $cookie_token;
//set cookie with rand value
setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
}
//set a sesison variable with request of www.example.com
if(!isset($_SESSION['request'])){
$_SESSION['request'] = -1;
}
//increment $_SESSION['request'] with 1 for each request at www.example.com
$_SESSION['request']++;
//verify if $_SESSION['user_token'] it's equal with $_COOKIE['user_token'] only for $_SESSION['request'] > 0
if($_SESSION['request'] > 0){
// if it's equal then regenerete value of token cookie if not then destroy_session
if($_SESSION['user_token'] === $_COOKIE['user_token']){
$cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));
$_SESSION['user_token'] = $cookie_token;
setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
}else{
//code for session_destroy
}
}
//prevent session hijaking with browser user agent
if(!isset($_SESSION['user_agent'])){
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
}
if($_SESSION['user_agent'] != $_SERVER['HTTP_USER_AGENT']){
die('session hijaking - user agent');
}
note: do not regenerate token cookie with ajax request
note: the code above is an example.
note: if users logout then the cookie token must be destroyed as well as the session
6 - it's not a good aproach to use user ip for preventing session hijaking because some users ip change with each request. THAT AFFECT VALID USERS
7 - personally I store session data in database , it's up to you what method you adopt
If you find mistake in my approach please correct me. If you have more ways to prevent session hyjaking please tell me.
Try Secure Cookie protocol described in this paper by Liu, Kovacs, Huang, and Gouda:
As stated in document:
A secure
cookie protocol that runs between a client and a server
needs to provide the following four services: authentication, confidentiality, integrity and anti-replay.
As for ease of deployment:
In terms of efficiency, our protocol does not involve any database
lookup or public key cryptography. In terms of deployability, our protocol can be easily deployed on an existing web server, and it does not require any change to
the Internet cookie specication.
In short: it is secure, lightweight, works for me just great.
Ensure you don't use incremting integers for session IDs. Much better to use a GUID, or some other long randomly generated character string.
There are many ways to create protection against session hijack, however all of them are either reducing user satisfaction or are not secure.
IP and/or X-FORWARDED-FOR checks. These work, and are pretty secure... but imagine the pain of users. They come to an office with WiFi, they get new IP address and lose the session. Got to log-in again.
User Agent checks. Same as above, new version of browser is out, and you lose a session. Additionally, these are really easy to "hack". It's trivial for hackers to send fake UA strings.
localStorage token. On log-on generate a token, store it in browser storage and store it to encrypted cookie (encrypted on server-side). This has no side-effects for user (localStorage persists through browser upgrades). It's not as secure - as it's just security through obscurity. Additionally you could add some logic (encryption/decryption) to JS to further obscure it.
Cookie reissuing. This is probably the right way to do it. The trick is to only allow one client to use a cookie at a time. So, active user will have cookie re-issued every hour or less. Old cookie is invalidated if new one is issued. Hacks are still possible, but much harder to do - either hacker or valid user will get access rejected.
AFAIK the session object is not accessible at the client, as it is stored at the web server. However, the session id is stored as a Cookie and it lets the web server track the user's session.
To prevent session hijacking using the session id, you can store a hashed string inside the session object, made using a combination of two attributes, remote addr and remote port, that can be accessed at the web server inside the request object. These attributes tie the user session to the browser where the user logged in.
If the user logs in from another browser or an incognito mode on the same system, the IP addr would remain the same, but the port will be different. Therefore, when the application is accessed, the user would be assigned a different session id by the web server.
Below is the code I have implemented and tested by copying the session id from one session into another. It works quite well. If there is a loophole, let me know how you simulated it.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
String sessionKey = (String) session.getAttribute("sessionkey");
String remoteAddr = request.getRemoteAddr();
int remotePort = request.getRemotePort();
String sha256Hex = DigestUtils.sha256Hex(remoteAddr + remotePort);
if (sessionKey == null || sessionKey.isEmpty()) {
session.setAttribute("sessionkey", sha256Hex);
// save mapping to memory to track which user attempted
Application.userSessionMap.put(sha256Hex, remoteAddr + remotePort);
} else if (!sha256Hex.equals(sessionKey)) {
session.invalidate();
response.getWriter().append(Application.userSessionMap.get(sessionKey));
response.getWriter().append(" attempted to hijack session id ").append(request.getRequestedSessionId());
response.getWriter().append("of user ").append(Application.userSessionMap.get(sha256Hex));
return;
}
response.getWriter().append("Valid Session\n");
}
I used the SHA-2 algorithm to hash the value using the example given at SHA-256 Hashing at baeldung
Looking forward to your comments.
Let us consider that during the login phase the client and server can agree on a secret salt value. Thereafter the server provides a count value with each update and expects the client to respond with the hash of the (secret salt + count). The potential hijacker does not have any way to obtain this secret salt value and thus cannot generate the next hash.
To reduce the risk you can also associate the originating IP with the session. That way an attacker has to be within the same private network to be able to use the session.
Checking referer headers can also be an option but those are more easily spoofed.
Use SSL only and instead of encrypting the HTTP_USER_AGENT in the session id and verifying it on every request, just store the HTTP_USER_AGENT string in your session db as well.
Now you only have a simple server based string compare with the ENV'HTTP_USER_AGENT'.
Or you can add a certain variation in your string compare to be more robust against browser version updates.
And you could reject certain HTTP_USER_AGENT id's. (empty ones i.e.)
Does not resolve the problem completley, but it adds at least a bit more complexity.
Another method could be using more sophisticated browser fingerprinting techniques and combine theyse values with the HTTP_USER_AGENT and send these values from time to time in a separate header values. But than you should encrypt the data in the session id itself.
But that makes it far more complex and raises the CPU usage for decryption on every request.
If ISP hijack the certificate-verification, ISP will possibly initiate a Man-in-the-middle attack. Especially with a compromised certificate authorities.
So I believe you can not prevent session hijack from ISP. Especially when legal forces come with a fake certificate got from CA under law enforce.
You will need something outside the network to protect your session, for example one time pad. This is why one time pad so sensitive and can only be sold by few companies.
Be careful, one time pad may be exploited. Choose your one time pad with profession.
Protect by:
$ip=$_SERVER['REMOTE_ADDER'];
$_SESSEION['ip']=$ip;

Resources