Node.js TCP socket sessions - node.js

I have multiple clients connecting to a Node.js TCP socket server from an app. I would like to know a secure way of managing their session.
Username + password is passed over the socket to the server. The server confirms this is correct.
I believe that I now need to generate a unique token to send back to the client.
Now if the user closes the app, then opens it again, this token can be passed to the server and thus the server will confirm the user is authenticated again.
Potentially though, this token could be used by somebody else to gain access to this persons account. Is there a way to prevent this?
Is there a more secure way (whilst still maintaining the ability for the user to authenticate without logging in again)?
How would you handle connections from other devices using the same login. Do they get a different token or the same token?
Many thanks.

It comes down to your definition of secure enough. What you're doing now, which is essentially session tracking, and it is generally secure enough for a lot of general purpose usages - however there is typically an extra component added in which is a session should only be considered valid for a particular ip. If the users' IP changes, you should make them login again and issue them a new token. That way if some bad guy hijacks their session id it won't do them any good.
Now of course this is only valid if your bad guy can't appear to come from the same IP address as your client. If you are concerned about bad guys who are behind the same NAT as your client, and thus can appear to come from the same IP, then you'll have to bump up your security a bit more and maybe consider a system similar to what SSH uses, but that's a bit more complex.
As for connections from multiple devices, it's up to you - you can either keep track of some single token and just hand back that token when the user logs in from a different IP (while at the same time now allowing both IPs to access the site using that same token), or you can just issue a fresh token every time someone authenticates. Personally I tend to find issuing fresh tokens easier, much less tracking and hassle... but it comes down to your application and how you want to organize things, I could dream up good use cases for both methods.
Also, as for doing the password exchange.. You should at least do some hashing there, ie, server sends client some random_string, client then uses some hash function (such as md5 or sha) to compute hash(random_string + hash(username + password)) and sends it back. The server then verifies that this matches by checking hash(random_string + password_hash) is equal to whatever the user sent it. This makes it so the user's plain text password never has to be stored anywhere - on the server you just store password_hash = hash(username+password) when the password changes.

Maybe something like this:
FIRST LOGIN:
username + pwd (hashed) ---> check user/hashed pwd
receive token <--- send token
NEXT LOGIN:
request login ---> receive request
receive random string <--- send random string
hash string with token as salt ---> compare hashed string
You should allow only one attempt with that random string and if possible check the IP from the original login.
This is not perfect because you could still intercept the token at login, but then you would also have the username and pwd.

This question basically comes down to session hijacking using stolen cookies. So the question is how you can secure the cookie as good as possible.
Use https instead of http and force your users to use https. This way, the cookie is not transmitted as clear text and can not be stolen using eavesdropping.
Set the secure attribute on cookies (see Wikipedia) to bind the cookie to https and avoid it from being transferred via http.
Use some kind of message authentication digest, such as HMAC, to make sure that the cookie has not been tampered.
Optionally you can embed the client's IP address into the cookie and only accept it if it is being sent from the specific IP. Unfortunately this may cause problems with proxy servers or dial-up connections where IP addresses are newly assigned from time to time.
Last but not least allow one token only to be used once at a given point in time. If the user logs on using the same token on a second machine, either disallow the connection or end the first machine's session.
Hope this helps ...
PS: I am very sorry, I skipped the TCP part. Of course most of what I wrote only applies to http, not TCP. Anyway, some things might help anyway, such as #3, #4 and #5.

Related

Trying to understand login systems and sessions

Im trying to understand how a user can keep logged (i'm trying to implement this on Node without frameworks, for learning). Just a couple of questions based on what i think i understand:
(1) When the user tries to login, it sends the user and password in an HTTP request body
(2) When data arrives to the server, it checks everything needed like if the user exists and if the password is correct
And here comes, i think, my problem: How can the user keep logged? The third step would be something like:
(3) The server create all the session data needed, encrypts and send it to the client?
(4) The clients store the encrypted data in the localstorage
(5) The credentials are sended with every request to the server, and the server decrypts it and check it before processing every user's action.
That's what i understand. But i find this very extrange. I feel i missing a lot... storing data in client side doesn't seems (at least for me) secure. Should the session data be stored on server-side? And how the username and password should be sended securely? It must be encrypted client-side? Is this secure? I think im looking for some pattern or i don't know. I feel lost.
Yeah, and sorry my bad english and poor knowledge. Im not asking for code and i will also appreciate any hint (like what to search in google, or a interesting blog) :)
Thank you, y un abrazo :)
--- EDIT ---
Well, finally i founded some usefull links and solved great part of my doubts :)
[http://stackoverflow.com/questions/6922145/what-is-the-difference-between-server-side-cookie-and-client-side-cookie][1]
[http://blog.codinghorror.com/protecting-your-cookies-httponly/][2]
[http://www.cse.msu.edu/~alexliu/publications/Cookie/cookie.pdf][3]
[https://es.wikipedia.org/wiki/Cookie_(inform%C3%A1tica)][4]
[https://newspaint.wordpress.com/2015/09/06/how-to-get-cookies-from-node-js-http-response/][5]
1 and 2 are correct.
Sessions are usually implemented using cookies, not client-side local storage, because cookies are automatically sent to the server with each request. The cookie will often contain just a long randomly generated ID which refers to data stored on the server side, e.g. in a database. This data will identify the user and possibly store other session-level settings.
It is also possible to use a cookie with signed (and possibly encrypted) user information - for instance ASP.NET does this by default. This has the benefit that no storage is required for the session. The downside is that sessions cannot easily be destroyed from the server side. Therefore e.g. a feature that shows the user their currently active sessions (from other devices) and allows them to log them out couldn't be implemented.
Sending the username and password over the Internet should preferably be done securely, by using HTTPS. Do not implement your own encryption on the client-side. It will likely not work, plus the cookies themselves are viable to be stolen if the connection is not properly encrypted and authenticated.

Authentication system - is my one secure?

I want to authenticate my users based entirely on cookies and sql db.
What I do is:
1. Once they login, I generate a random string, create a hash from it, save it in the database along with the user id and his IP.
2. I send the hash to the user as cookie
3. Whenever he wants to access something, I verify if his cookie hash matches the one on the server and also if his IP matches. Of yes, he is valid or else, log him out.
4. (As pointed by Akhil) If he clears his browser cookies or anything does not match the information on the database, I clear all the rows with his username and log him out.
Note: I use a session cookie for storing the random hash, which again is generated using the timestamp, and as long as time doesn't repeat itself(I believe), its random in the corect way.
Is this fine? How can I make it better?
Once they login, I generate a random string
Make sure you use a cryptographically secure method to generate the random string. Do not use mt_rand use something such as openssl_random_pseudo_bytes.
create a hash from it,
Make sure to use a secure hashing algorithm (not MD5, and at least SHA-2).
save it in the database along with the user id and his IP.
One thing to bear in mind is that some internet connections share IP addresses or will sometimes change the client IP address (e.g. AOL or mobile).
I send the hash to the user as cookie 3. Whenever he wants to access something, I verify if his cookie hash matches the one on the server and also if his IP matches. Of yes, he is valid or else, log him out.
It sounds like a good way of doing it and there are no flaws in itself. I would implement a session timeout mechanism. For example, store the date last used in the DB for a sliding expiration and the query will only query records that have not expired. You could have a background process that runs to clear out old, expired records.
Also, use HTTPS and set the Secure and HttpOnly flags on the cookie. This will prevent them being leaked over HTTP, but I would not go as far as disabling HTTP on your system as there are workarounds for an attacker if it is anyway.
I would not be concerned with the cookie being stolen by another user on the same machine. If the cookie can be stolen in this way then the user's machine is probably compromised anyway and you cannot make your system protect data that is outside of your control. You could however renew the token (random string) on a periodic basis giving the user a rolling cookie. You would have to ensure only one user can be logged in at once under the same account though for this to be effective.
Your method only makes sure that the user possess the random string you generated and is using the same external IP address. There exists several way of abusing this system:
if your website doesn't enforce HTTPS then a user connecting using an unsecured public WiFi network could be at risk: if another user of the WiFi network is listening to all the packets being sent on the network, he could intercept your cookie and use it to access the website as your legitimate user. Your server would be unable to differentiate them because they'll both use the same IP address... (There is a Firefox extension available which enable anyone to intercept such login cookie easily: http://en.wikipedia.org/wiki/Firesheep)
This system is also more generally vulnerable to man in the middle attacks (without HTTPS)
If your cookie is stored on the user computer's hard drive it could be reused by another user.
So to answer your question, your system can be deemed as secured provided a few conditions:
you enforce the use of HTTPS on your website (unencrypted HTTP connections should be refused)
your random string is truly random (there exist right and wrong ways of generating random strings in PHP)
your cookie has a short expiry and preferably is set as a session cookie.
You should take a look at the following related question providing details about the proper way of doing what you want to do: How to secure an authentication cookie without SSL
One cannot say this is "bad". But in Web Development, and specifically in its security domain relativity talks. I recommend you to download a CodeIgniter (google it for more info) Session Class (standalone version) and use it. The basic idea is the same as yours, but it is properly more mature since it is developed in such a famous php framework. You can do your DB operations within that class too, since it allows session saving to DB.

Sanity Check: SSL+ POST vs. un-encrypted GET

A classic dumb thing to do is pass something security related info via a GET on the query string ala:
http://foo?SecretFilterUsedForSecurity=username
...any yahoo can just use Fiddler or somesuch to see what's going on....
How safe is it to pass this info to an app server(running SSL) via a POST, however? This link from the Fiddler website seems to indicate one can decrypt HTTPS traffic:
http://fiddler2.com/documentation/Configure-Fiddler/Tasks/DecryptHTTPS
So is this equally dumb if the goal is to make sure the client can't capture / read information you'd prefer them not to? It seems like it is.
Thanks.
Yes, it's "equally dumb". SSL only protects data from being read by a third party; it does not prevent the client (or the server) from reading it. If you do not trust the client to read some data, they should not be given access to that data, even just to make a POST.
Yes, any user can easily examine the data in a POST request, even over HTTPS/SSL, using software like Burp Suite, Webscarab, or Paros Proxy. These proxies will complete the SSL transaction with the server, and then pass on the data to the client. All data passing through the proxy is stored and is visible to the client.
Perhaps you are trying to store sensitive/secret data on the client-side to lighten the load on your server? the way to do this so that the user cannot look at it (or change it) even with a proxy, is to encrypt it with a strong symmetrical secret key known only to the server. If you want to be sure that the encrypted data is not tampered with, throw on an HMAC. Make sure you use a sufficiently random key and a strong encryption algorithm and key length such as AES 256.
If you do this you can offload the storage of this data to the client but still have assurance that it has not changed since the server last saw it, and the client was not able to look at it.
This depends on who you're trying to protect your data from, and how much control you have over the client software. Fundamentally, in any client-server application the client must know what it is sending to the server.
If implemented properly, SSL will prevent any intermediary sniffing or altering the traffic without modifying the client. However, this relies on the connection being encrypted with a valid certificate for the server domain, and on the client refusing to act if this is not the case. Given that condition, the connection can only be decrypted by someone holding the private key for that SSL certificate.
If your "client" is just a web browser, this means that third parties (e.g. at a public wi-fi location) can't intercept the data without alerting the person using the site that something is suspicious. However, it doesn't stop a user deliberately by-passing that prompt in their browser in order to sniff the traffic themselves.
If your client is a custom, binary, application, things are a little safer against "nosy" users: in order to inspect the traffic, they would have to modify the client to by-pass your certificate checks (e.g. by changing the target URL, or tricking the app to trust a forged certificate).
In short, nothing can completely stop a determined user sniffing their own traffic (although you can make it harder) but properly implemented SSL will stop third-parties intercepting traffic.
The other, more important reason not to add confidential information into URL with GET requests is that the web server and any proxies on the way will log it. POST parameters don't get logged by default.
You don't want your passwords to show up in server logs - logs are usually protected much, much less than, for example, the password database itself.

Exists a way to prevent cookies from getting stolen?

in Web 2.0 applications many users usually want to stay logged in ('remember me' flag) and on the other hand their cookie can give access to very private data. Is there a way to prevent that somebody who steals the cookie - directly from the computer or via sniffing - can use the cookie to get access to the user's data? Always HTTPS is not an option.
Thanks, Bernd
[Edit] Connect the IP address to the cookie is not an option either.
KISS -- just use sessions so that you're using an ID that is already automatically created by the server-side scripting language of your choice. That's hard enough to guess. Then, if it's stolen, store the IP address and user-agent of the visitor in the session (making sure never to output that) and only consider the session valid only if the already stored IP address and user agent match that which is found for the remote client.
In this scenario, the attacker would have to do the following three things:
Steal the victim's cookies
Spoof the correct IP address
Spoof the correct User Agent
It also helps to make sure that the attacker doesn't already know all of the things he/she would have to do in order to correctly take over a victim's session. IE: They may assume just the cookie is needed and then fail... and have to figure out everything else through a very long trial and error. In this way, you gain security through obscurity and through difficulty, depending on the skill of the attacker and his/her existing knowledge of the system.
Bernd -- the trouble with anything done over standard HTTP is that it's plaintext; anyone can fake anything. IP Spoofing is a bit more challenging to do than just plain cookie stealing, so tying to the IP tends to be what people do. Like you said, that does not work very well with highly dynamic environments.
The only mostly secure way I can think of is to use HTTPS to place and verify a "permanent" cookie, and then place (in the same HTTPS session) a short-lived session cookie. The rest of the communication can be done over regular HTTP, using the session cookie to authenticate.
That way, fewer resources are used in supporting encryption (just the handshake), the permanent cookie is not exposed -- it's only transmitted under encryption -- and stealing the session cookie opens up to only limited risk, since that cookie will quickly expire.
All that being said -- don't let users click "remember me" on a site that contains truly sensitive data! That's why Banks don't do it..
Hope this helps.
About storing complex cookie ids and associated IPs in a database -- you don't really have to do that. If you have a secret key K, it is enough to encrypt the user's ip with your K, and place the result {IP}K as a cookie. As long as your key is secure (and the crypto hasn't been broken -- but if that happens, we have bigger problems), this is safe.
Perhaps using a Session ID and token (a hash based on the IP, a salt, and the Session ID), that is regenerated every request (use a fast hashing algorithm) would be a good approach? I store session data in a database (currently), and this means I have a two query overhead every request. It works like this:
Select where SID and TOK match.
Verify a token generated based on current client matches that in the database.
deserialise the data into a property.
Scripts etc happening.
Serialise the updated data, regenerate the SID/TOK, and update DB where SID/TOK = old sid and tok, updated data and new sid and tok. Set the cookie to the new SID and TOK.
In this way, firstly cookies are bound to whatever I base the token on (in this case, remote address), and if that is stolen and client data spoofed, the cookie is only useful for one request anyway - by the time the cookie is intercepted, it is useless.
The only perceivable weakness I can see is if the attacker managed to grab a cookie, spoof, and use it, before the real person could do another request. There are a few ways to solve this that I need to think about. The overhead is two queries and generating a token hash twice (once for verification, once for replacement).
Put a lid on the cookie jar.
Jokes aside, the best option has already been stated - make the cookie an obscure ID and tie it to an IP address lookup on the server side. Since you edited to say you cannot tie it to an IP address, that leaves the obscure ID part. Your options are limited with cookies - the minute you place something on the client, it becomes a risk.
Store a cookie that is an obscure ID into your local server database. Do a server-side DB lookup based on the ID provided in the cookie. Be sure to make the ID sufficiently complex that it cannot be easily guessed. Map the ID to the IP Address of the user. If their IP changes, then force them to log in again, and create a new ID.
On second read, it sounds like you want a high level of security with your hands tied. The user must have the choice to remain logged in and thus increase his/her risk. You can implement all the security in the world from the application's and server's point of view, but if the user forgets their laptop on a table in Tim Horton's (Canadian Starbucks), then none of it will do you any good.
Leave the choice up to the user as to whether or not they remain logged in, and give them a warning about their information being at risk.
Bernd - you say connecting the IP address to the cookie is not an option, I'm assuming that's b/c the user could be connected via DHCP, and thus could come in under a different IP each time. Have you considered tying the cookie to the DNS host name? You could encrypt the cookie using a private key, and store it on the user's box. Then whenever they come in, check the cookie, un-encrypt it, and then check the user's current DNS Host name against the one in the cookie. If it matches, you allow them in. If not, you don't allow the auto-login.
FYI - in ASP.Net, to get the DNS host name of the user's box, just look at
Page.Request.UserHostName

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