Threads such as PHP Session Fixation / Hijacking and some people like Chris Shiflett recommend checking the user agent ( $_SESSION['HTTP_USER_AGENT'] ) to help check for session validity. Some resources even recommend something like this:
<?php
$string = $_SERVER['HTTP_USER_AGENT'];
$string .= 'SHIFLETT';
/* Add any other data that is consistent */
$fingerprint = md5($string);
?>
However, Chris Snyder says that "the universe of browser agents is miniscule in comparison to the universe of users, so it is impossible for each user to have an individual user agent. Furthermore, it isn't hard to spoof a user agent. And so there is little real point in checking this metric as a proof of session validity" (Chapter 7, pg 103).
It's very difficult to know what to do when one encounters conflicting advice, and when some of the advice may be out-of-date (such as the Shiflett/PHPSec example above, whose timestamp seems to be Friday, March 18, 2005). Newer advice such as Snyder's (date of publication: December 9, 2010) would seem to be better, but is this always so? (For example, in spite of spending a lot of time recommending the use of mysqli, Snyder completely ignores what Stack Overflow users seem to agree is the better choice -- PDO -- so I'm not totally sold on Snyder as the ultimate Trustworthy Expert).
So I guess my question has two parts, one specific (should I bother examining the user agent?) and one more general (whose advice should I trust when it comes to the latest thinking in PHP Security?), with my obvious bias being "trust the people on Stack Overflow!" -- or I wouldn't be asking in the first place, because crowd-sourcing the most current thinking is often the best idea.
Following useful discussion in the comments w/ #Radu, to clarify the HTTPS question --
Snyder seems to be saying two things: 1.) HTTPS makes other tools less necessary or unnecessary. 2.) In situations where one cannot use HTTPS, it is still not really useful to check the user agent (and this seems to be the point where he disagrees with some possibly older advice).
If the man in the middle can hijack the session ID, then he should have absolutely no problem in sending the same user agent, so I don't think this will get you anywhere. This is security by obscurity.
If you want real protection, use HTTPS.
The user agent is not totally fool proof by itself but it adds thin layer on top of other security. There is debate on what should go to fingerprint. But nonetheless it adds to defense-on-depth principle. You can set a cookie and use that in combination with user agent and specific word to make fingerprint.
My point is, it adds to security though little!
The problem with sessions is that you just need the session ID to use that session. If you know a valid session ID, you can use that session. That’s how sessions work.
Now some suggest to use an additional datum to verify the validity to use a certain session by a certain client. The user agent ID is such a suggested datum as it is some kind of unique (see Panoptoclick to test your own) and doesn’t change during the session (in opposite to some other informations provided by the client).
But this only lowers the likelihood of Session Fixation and prevents the accidental use of a foreign session in case of Session Hijacking. Because an attacker could try to obtain the victim’s user agent ID and spoof it when fixing or hijacking the session. And here it doesn’t matter whether you store the user agent ID in plain or as a (salted) hash.
Better use the already proven protection measures.
Related
Refer to my question I posted on Stack Security, I am using
md5($this->_user_agent.self::SECURE_SESSION . $this->_ip_address);
for generating a session fingerprint( and also as a added security measure regenerating Session Id after every 10 minutes). It looks fine, but I want to deal with a situation when a attacker exploit an victim in the same network[ip address], uses the same User agent , and has also stolen the Session ID, now my above check will fail in that case.
Few solution I found while Googling around are using Flash Cookies or EverCookies or a jquery browser fingerprinting plugin. But all of this got their own pros and cons. Also I want to avoid Flash Cookies etc ( as it violates EU laws & intrude User's privacy also at same time).
So as a workaround I have modified my fingerprinting a bit and added php_uname(), PHP_OS in it .
Now my above fingerprinting method became,
md5($this->_user_agent. self::SECURE_SESSION . $this->_ip_address.php_uname().PHP_OS);
I know it is not also a Robust or Complete solution, but does adding the two more constraints made my fingerprinting a bit more Secure or has got no impact at all ?
PS: I am posting it here instead of security , as I think reach of
statckoverflow is more .
I usually hang out in a community that uses a bulletin board software.
I was looking at what this software saves as cookie in my browser.
As you can see it saves 6 cookies. Amongst them, what I consider to be important for authentification are:
ngisessionhash: hash of the current session
ngipassword: hash (not the plain password probably) of the password
ngiuserid: user's id
Those are my assumptions of course. I don't know for sure if ngilastactivity and ngilastvisit are used for the same reason.
My question is: why use all these cookie for authentication? My guess would be that maybe generating a session hash would be to easy so using the hashedpassword and userid adds security but what about cookie spoofing? I'm basically leaving on the client all fundamental informations.
What do you think?
UPDATE #1
The contents of these cookies are what I think they contains. I'm not sure about it.
Of course if call a cookie ngivbpassword and contains an hash, my guess is hashedpassword. Probably it could be password+salt.
My main concern is about these solution giving to much information when under a cookie spoofing attack.
UPDATE #2
This question doesn't want to criticize the way these specific software works but, thorugh these answers I want just to learn more about securing software in a web environment.
This happens because session and login cookies may have different lifecycles.
Imagine website with millions of users every day. The website won't store your session for a year just to log you back the next time you get back.
They use login cookies for that.
These cookies are also called Remember-Me cookies.
Sessions are not persistent. Cookies are.
Update #1: I haven't worked with vBullettin but it looks like the classical "Remember me" feature.
Update #2:
Yeah, it's a remember me feature, I'm
asking why they're doing it in that
way
Alright... How do you implement a "Remember me" feature? You obviously need to use cookies, I assume that's clear. Now, what do you store?
The naivest way is to store user and password in clear text and perform regular authentication. It's among the most insecure mechanisms you can use yet some sites actually do it that way.
Second slightly less naive way is to store a hash of the user and password and perform a modified version of the regular authentication. Is not as bad as the previous method but it still suffers from some issues; for instance, there's no effective way to disable or expire a saved cookie from the server.
Third way is to keep a database table with "remembered" sessions, identify each one with a long unique string and store such string in the cookie. The string can be random or calculated but, of course, randomness has the advantage that the string cannot be guessed even if you know the algorithm.
Further security can be accomplishes by storing dates, IP addresses and other piece of data in the server.
As I said, I know nothing about vBulleting but it seems they're using method 2 or method 3.
Update #3:
The contents of these cookies are what
I think they contains. I'm not sure
about it. Of course if call a cookie
ngivbpassword and contains an hash, my
guess is hashedpassword. Probably it
could be password+salt.[...] My main
concern is about these solution giving
to much information when under a
cookie spoofing attack.
A successfully cookie spoofing allows you to fully impersonate the user so you can just enter the control panel and enjoy the free buffet, thus making the cookie content irrelevant.
Whether they store a salted password or it's just a name it's something I don't know.
Here is a question, what are your concerns? Are you building some kind of authentication system?
I also think that having the user id and password in cookies can be a security issue.
is user id encoded or an integer?
Cookies should be as-small-as-they-can peace of information about who you are on the server.
Sessionhash, session_id or sid is unique ID of you (your session on the server). The rest of cookies can be easily hidden on the server side.
Holding password hash in cookies is a security issue. You should avoid that.
Last 4 cookies comes from google ads.
PS. Most bulletin boards are not so great software anyway.
I use HTTPS, but want to minimize the risk of someone evil crafting their own cookies with a session ID that someone else actually uses recently.
As a session variable I have an expiry time so the session is invalidated if it hasn't been used recently, so I figure the window of opportunity is when the victim is active or recently left the site without logging out properly.
I don't expect huge amounts of traffic and I use the standard php methods of generating session IDs. I believe the "risk" of someone actually succeed (or even try) hijacking someones session here is close to zero.
What I would want to do is to "identify" the remote user somehow, without using $_SERVER['REMOTE_ADDR']. My thoughts being that the attacker would have to both find a valid session ID, as well as impersonating the different properties of the actual user.
I don't want to force the user to use a certificate to log in. I want it to work in all standard web browsers, even for my grandmother and other non tech-people like her.
So, what I originally wanted to ask was: are there any "properties" of the HTTPS session that could be used? Would they be useful? If so, how do I find them? phpinfo() reveals nothing HTTPS specific. (Is it because httpd doesn't expose it?)
Should I just use a concatenation of HTTP_USER_AGENT + HTTP_ACCEPT + HTTP_ACCEPT_LANGUAGE + HTTP_ACCEPT_ENCODING + HTTP_ACCEPT_CHARSET or something similar that is assumed to be unique enough between users?
Very happy for all answers! (But please read the question before answering with only referrals to other questions on StackOverflow)
Thank you!
You need to ensure that you've got both the secure and http_only flags set on your session cookies. You also need to change the session_id when a user authenticates to avoid session fixation problems.
While what you propose should be relatively safe in terms of finger-printing, it's not really all that selective - otoh there are lots things which should NOT be used for fingerprinting (like CLIENT_ADDRESS) so its not really very easy to suggest something better.
Apart from my suggestions above, I'd recommend spending your time looking at other potential security problems.
C.
The OWASP top 10 is an excellent aid for this class of attacks.
Specificlly you need to worry about these three:
A2: Cross-Site Scripting (XSS)
A3: Broken Authentication and Session Management.
A5: Cross-Site Request Forgery (CSRF) (Also known as "Session Riding")
Almost all of the $_SESSION variables are known to the attacker and can be any value. There is no point is checking these variables as it is trivial for an attacker influence them. A important exception is $_SERVER['remote_addr'] which is pulled directly from apache's TCP socket, thus this value cannot be spoofed or otherwise tampered with. However, if the attacker is on the same network segment (such as if he was sipping a cup of coffee right behind you at the cafe), then the attacker would have the same ip address.
I am creating a login system for a web application using PHP. My question is, is it safe to only store the user login information in the current session? For example, if a user named John logs in successfully to my site, can I just store $_SESSION['Username'] = 'John' and $_SESSION['LoggedIn'] = 1 then check that $_SESSION['LoggedIn'] is equal to 1 on each page to verify the user is actually logged in? Or is there a better way to do this? I am not aware of any problems this may cause off the top of my head, but I wanted to make sure I wasn't leaving a big hole in my site that would cause problems down the road.
Also, I am storing an md5 hash of the user's password + salt in the database, not their actual string password so that is one less thing to worry about.
Let me know if you need any more information or if this is not clear. Thanks!
That's a perfectly reasonable approach. Your visitors will never be able to edit the session data on your server (unless the server itself is insecure, in which case anything's fair game), so a LoggedIn=1 value in the session is perfectly safe.
However, do keep in mind the risk that one visitor hijacks the session of another (by stealing the session key). One way to help protect against this is to also store the visitor's IP address (from $_SERVER['REMOTE_ADDR']) in the session and then in later requests confirm that it hasn't changed.
There are a number of risks to consider:
Session hijacking: this is where someone steals the user's cookie and pretends to be them. Some will suggest IP filtering to counter this but that can have awkward side effects. People use Websites from mobile devices or on laptops that are used at work, home and at wifi hotspots and there are other cases where IP addresses can change. So my advice is only do this for highly sensitive Websites (eg online banking);
Your Site is Compromised: in this case the user will have access to your database anyway so there is no extra risk with storing authentication information in the session. They can just as easily change who they are by issuing UPDATE statements to your database;
A Co-Hosted Site is Compromised: if you use shared hosting, a completely unrelated site could put you at risk (with or without this scheme) because a bunch of sites are all running on the same Apache instance and can thus access each other's files (although it can be hard to figure out what site they belong to). So if a site you've never heard of is hacked it can impact your site;
A Co-Hosted Site is Malicious: similar to (3) except the threat is internal but is otherwise similar.
So I'd say it's fine (subject to (2)) but just be aware of the risks. Follow, at a minimum, these best practices:
Never store unencrypted passwords;
Use a strong hashing algorithm (SHA1 preferred or MD5 at least);
Make sure authentication cookies expire at some point. How long depends on your site. It could be a week or two or an hour or two of inactivity or both.
Consider SHA1 or an even stronger hash instead of MD5. You're salting it, though, that's good.
Back to your question: yes, that's fine. However, implement measures to make sure sessions are not hijacked. Wikipedia actually has a fairly good article on it.
In most of the systems I've written, I've included logic to verify the remote IP hasn't changed. You can store that in the session, too, since the session vars don't get passed to the user (only the session ID). If you really want to get creative, you can add other checks -- user-agent, and what not.
You also have to account for session attacks. Check referrers. If you have a disastrous operation, let's call it a POST to DeleteMyAccount, I can write a form submission plus javascript to hit DeleteMyAccount in a forum post on an unrelated site, counting on that session to be present in the user's information.
Sounds OK; you may want to think about setting an expiry time (so if someone walks away and leaves the browser open they're not in too much danger).
On the whole, you are definitely on the right track. I would recommend you use IDs for your users in the session rather than the username as IDs are a better unique reference inside your code.
Also, md5 is not considered strong enough for password hashing anymore: it's is too fast to hash and you don't want that in a check that an attacker will need to run over and over again (whilst a real user only needs to do it once). I wish I could find the reference, but leading edge wisdom is to do lots of rounds of a leading edge hashing algorithm, like sha512.
You can use COOKIE instead of SESSION variable. you may set COOKIE by following
setcookie('ID', $variable, time()+8*60*60);
You have to be aware about SQL Injection. When you Insert or Update your database where user textbox relates please be aware about SQL Injection. Insert / Update your values by htmlentities() function.
I've developed my website that checks if the user is registered and creates a session variable with the username. It's all that is stored as a session variable. If I want to protect my pages (so that only registered users may see them), I check if the session variable is set.
Is this secure?
Or can you give a more secure method?
Generally, the Session is server side, but If I somehow get the Session ID I can just hijack it.
I'd recommend at least storing either the IP and maybe also the User-Agent, and in case of mismatch, invalidate the Session.
Basically, you are fine with storing whatever you want in Session. The only caveats are:
if you are not using secured connections (like SSL), the sessionId can be sniffed and hijacked. This is of no importance because the username and pass can also be hijacked, and you are subject to "man in the middle" attacks, etc. So basically, your system is fine and provides low security without SSL.
In the articles on PHP they mention some concerns with shared hosting and session hijacking. I'm not sure if this is true, so I've posted a question here. Edit: This concern seems to be real, so you'll have to use one of the workarounds for storing session (e.g., database) if you use PHP.
In general, though, most of the security concerns mentioned (including XSS attacks) are not with storing stuff in Session but rather general security concerns. Storing userid -- or some encrypted form of the same -- in Session is generally quite secure.
Most importantly: if you were to use your own algorithm to generate a random cookie code for each user, that would no doubt have more security flaws (not being an expert) than the session-key generation algorithms of PHP, ASP.NET, Rails, whatever...
I could find a more appropriate Bruce Schneier quote, but this one will do, "No one can duplicate the confidence that RSA offers after 20 years of cryptanalytic review.”