4

Scenario:

  • After a user has logged in, a session variable is set confirming their login.
  • At the top of every page, login session variable is confirmed valid
  • If it's not, they're booted out.
  • No persistent cookies are used, only session

Question:

Is this a strong enough security measure by itself, or should I

  • Set two session variables to validate eachother and/or
  • Implement database/hash validation
  • ...?

========

(Incidentally, while I was researching this question, this wiki is a fantastic read.)

Community
  • 1
  • 1
Ben
  • 47,286
  • 44
  • 159
  • 208

5 Answers5

7

It is enough to store just user login (or user id) in the session.

To prevent session fixation/hijacking everything you need is just to implement simple algorythm (pseudocode):

if (!isset($_SESSION['hash']) {
    $_SESSION['hash'] = md5(!empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'no ua');
} else if ($_SESSION['hash'] != md5(!empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'no ua')) {
    session_regenerate_id();
    $_SESSION = array();
    $_SESSION['hash'] = md5(!empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'no ua');
}

You could move the hash calculation into some function to prevent of duplication, i've just shown a sketch of possible protection.

This is how I implemented this kind of protection in my kohana session class:

abstract class Session extends Kohana_Session
{
    public function read($id = null)
    {
        parent::read($id);

        $hash = $this->calculateHash();
        $sessionHash = $this->get('session_fixation');

        if (!$sessionHash) {
            $this->set('session_fixation', $hash);
        } elseif ($sessionHash != $hash) {
            $this->regenerate();
            $_SESSION = array();
            $this->set('session_fixation', $hash);
        }
    }

    private function calculateHash()
    {
        $ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
        $ua = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'no ua';
        $charset = !empty($_SERVER['HTTP_ACCEPT_CHARSET']) ? $_SERVER['HTTP_ACCEPT_CHARSET'] : 'no charset';
        $ip = substr($ip, 0, strrpos($ip, '.') - 1);
        return md5($ua . $ip . $charset);
    }
}
zerkms
  • 230,357
  • 57
  • 408
  • 498
  • About to accept but sorry, but I feel like I need more. A reference or two, some arguments regarding spoofing/hijacking...it seems weak to me to just use one variable for the whole auth procedure. – Ben Nov 12 '10 at 08:00
  • Hijacking of what?! Session is a data completely stored on the server. – zerkms Nov 12 '10 at 10:07
  • @Steve: I just asked specifically on your specific question "Is this a strong enough security measure by itself". Another comment with a link to `shiflett.org` has several **pointless** advices. The author sometimes has no idea what is he talking about. – zerkms Nov 12 '10 at 10:12
  • @zerkms - The article basically recommends fingerprinting a session with hashes generated from various headers, which is what you're doing. You also provide no support regarding why he "has no idea what he is talking about". – Ben Nov 15 '10 at 01:19
  • @Steve: because **it is pointless** to add: a) 'somethingSecret' b) `session_id()` to the fingerprint. These variables do not add any security. The autor cannot understand this, that is why he has no idea. – zerkms Nov 15 '10 at 01:23
  • @Steve: if you can't get why it is pointless: a) the secret string is the same for all sessions b) session_id is the same, if the session has been hijacking (surprise!). So this 2 parts **are constant** and **not related to UA**. The only way to protect from session hijacking and session fixation - is to use any specific data that **identifies** useragent. Look at my solution. – zerkms Nov 15 '10 at 01:26
  • @zerkms - Thanks for the explanation, it helps. So, to answer my original question, a single session variable is not enough security? – Ben Nov 15 '10 at 01:39
  • @Steve: look at the code I provided. To protect from the hijacking/fixation - you should rely on some user-specific data. In my code I follow user's IP, user-agent name, and the user-agent's accept charset. You are free to choose another variables, but I think that the ones I've chosen are good enough. In answer to your original question: – zerkms Nov 15 '10 at 01:43
  • @Steve: it depends on "enough for what". To detect that someone who holds the current session (the valid user, or hacker) has been authenticated - then yes, it is enough. But if you want to protect your sessions from being stolen - you need to follow some algorythm similar to mine. – zerkms Nov 15 '10 at 01:44
  • @z - Stop ordering me to look at the code you provided. Of course I am looking at it, which is why we are having this conversation. You have finally provided a straight answer to my question, so you get a +1. See my answer for details. – Ben Nov 15 '10 at 01:49
  • @zerkms By answer, he means answer to this question. (scroll down) You're right in saying that all you can do is add more variables (**real** variables that relate to a UA's request), though note that `md5` ing them adds nothing (`$_SESSION` storage is not *that* limited, and the cost of MD5 might be greater than the cost of storing and comparing a longer string). – Asherah Nov 16 '10 at 07:06
  • @Arlen Cuss: I know what answer is, but there was no answer when he wrote his comment and when I answered "what answer?" ;-) – zerkms Nov 16 '10 at 07:49
2

Don't try to write your own session scheme, PHP will do it better.

yes you can add more information to your $_SESSION to help prevent session hijacking

for example I generate a fingerprint by combining a secret phrase or random data with the user agent and the session_id() and hash it all. To hijack a session the user would need to figure out a valid session_id, and the hash of the fingerprint. it will look like this. This is a good read

$_SESSION['fingerprint'] = md5('somethingSecret' . $_SERVER['HTTP_USER_AGENT']. session_id());

then you would validate the session like

$check_print = md5('somethingSecret' . $_SERVER['HTTP_USER_AGENT']. session_id());

if($check_print != $_SESSION['fingerprint'] || $_SESSION['authenticated']){ 
    //invalid session
}
Brian
  • 4,030
  • 12
  • 53
  • 96
  • what is the role of string 'somethingSecret' here and why do you need `session_id()` in the fingerprint? – zerkms Nov 12 '10 at 10:09
  • I recommend reading that article that I posted but basically lets say some malicious user figures out an active session_id and sends it to the server in attempt to get access to an account, he would also have to present the fingerprint hash which would be virtually impossible to guess, he would have to know A)the secret string B) the browser the legit user was using and C) the session_id and would have to know to use an md5 hash and present that as part of the session. – Brian Nov 13 '10 at 02:07
  • @Brian - +1 for a decent reference – Ben Nov 15 '10 at 01:29
  • 1
    @Brian Perin: wrong. Atacker shouldn't know the secret string, because it is constant and applied to each fingerprint automatically. The only reasonable variable there - is a useragent. Other variables is just a trash. – zerkms Nov 15 '10 at 01:34
  • @Brian Perin: if you want to continue the discussion, answer the question: if we remove user-agent part from the fingerprint - will that algorythm protect from the session hijacking? If no - what is the reason to add the parts, that doesn't secure. If yes - prove that. – zerkms Nov 15 '10 at 01:36
  • the user doesn't know the secret, thats the point. In fact i dont even use a phrase its just a randomly generated salt. – Brian Nov 16 '10 at 11:20
1

As of 15 November, the two answers I have received do not address my question, which was

"Is this [a single session variable] a strong enough security measure by itself?"

This question says yes, but there seems to be some dissension. Here is a summary of the various results:

1) A single session variable is not enough security since a session can be hijacked fairly easily.

2) Since this can occur, no session is truly safe, but it can be made safer with the addition of a fingerprint. This ensures a unique, repeat-able check each time a session needs validation. @zerkms recommends a hash of User-Agent and a few others (refer to his code).

3) Salting the fingerprint is mostly useless since it obscures the data but is replicated on every client machine, therefore losing its unique-ness.

4) A database solution is useless since it is a client-side problem.

Not the definitive answer I was looking for, but I suppose it will have to do, for lack of anything better.

Reading that has helped/confused me further:

Session hijacking and PHP

Is HTTPS the only defense against Session Hijacking in an open network?

Community
  • 1
  • 1
Ben
  • 47,286
  • 44
  • 159
  • 208
1

There is nothing you can do, except use HTTPS.

It doesn't matter how many cookies you add or what data you hash; it can all be sniffed and sent back to the server.

If you're going to force a user to use a single UA throughout the life of their request, that can help: you don't need any special hashing business, because you're hashing it into $_SESSION which neither the user nor the hijacker can access directly, so why bother hashing it? Might as well just store $_SESSION["reportedUA"] = $_SERVER["HTTP_USER_AGENT"] on log-in and then check reportedUA on each request.

That, too, is trivial to hijack, once you realise it's happening, as you need only sniff the reported UA when you sniff the session cookie, and start using that.

What next? IP address? Session hijacking might be happening from behind a NAT, in which case you're screwed. Your users might be using dial-up, in which case they're screwed.

This problem has no solution: there is no way. There couldn't be a way. If a hacker can see your session cookies, then they can mess you up, because there's no additional information or challenge related to something only the user knows (i.e. password) that's sent with each request.

The only way to make the session secure is to secure the entire session.

Asherah
  • 17,443
  • 4
  • 51
  • 72
0

Is this a strong enough security measure by itself,

Set two session variables to validate eachother and/or
Implement database/hash validation

No, and the reason is this: Anything that your valid user can send to your server for authentication (Session ID, cookies, some hashed string, anything!) can be sniffed by others if it's not encrypted. Even if the server processes the data with md5 hashing, salt, double-session-variable checks, id or whatever, and stores that information, it is easily reproduced by the server when it receives the spoofed data again from some other source.

As many people have suggested, SSL is the only way to prevent this type of evesdropping.

It has occurred to me that, were the server to generate a new session id for each request, and allow the browser to reply with it only once, there could theoretically be only one hijacker request or post before the server and the authorized browser knew about it. Still unacceptable, though, 'cause one is enough to do serious damage.

Hey what about this:

Create a single-use GUID and random salt and encrypt it with a shared password using PHP - this is sent as the session id or a cookie.

The client receives the cookie, decrypts it with the shared password using javascript (there are many enc/dec utilities available)

Set the current cookie or session id to the GUID.

That would ensure that nobody could hijack the session unless they knew the password, which is never sent over the network.

SSL seems much easier, and is more secure still.

EDIT: Ok, it's been done - nevermind ;-)

Community
  • 1
  • 1
alfadog67
  • 813
  • 7
  • 27