Session fixation idea

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

Threaded View
I had this idea about preventing session fixation, and I'm wondering
what anyone else thinks about it.  The idea is, essentially, don't
allow session ids that YOUR PHP didn't generate (and aren't yet
expired) to log in.  That way if someone sticks a made-up session
ID on a URL, it won't matter, unless it happens to correspond to
an active session (guessing a user password is probably easier).

Is this already standard practice, new, or is there something better?

I like to use a session save handler to put the data into a database.
So in my case, using a session save handler isn't a lot of extra
work.  My approach can deal with sessions saved just about anywhere,
but you need explicit handlers you can modify to use it.

This approach assumes that you already have a login setup and every
protected page will check for a logged-in and unexpired session,
and if not, redirect the browser to the login page.  There's some
session data like $_SESSION['logged_in'] = 1 to indicate a valid
login.  You also have a timeout so the session expires some time
(say, an hour) after the last click.

When the session handler "open" routine is called, session_id()
returns the session ID the browser supplied if there was one,
otherwise it returns an empty string.  The documentation says this
but not specifically about what happens when it's called from the
session handler open routine.

So, if session_id() returns a non-empty string, and
that session doesn't exist in current sessions, you have
(1) A session-fixation attempt,
(2) A user returning after their session has expired (or they logged  
out explicitly) and been deleted.
Either way, there is no existing session data, so they can't be logged in.
I propose setting a global variable like $possible_session_fixation_attempt
to either 0 or 1 depending on the results of this check.

If the user tries to go anywhere but the login page, he's not logged
in, so he'll be redirected to the login page.  At the login page,
if $possible_session_fixation_attempt is set, call
session_regenerate_id(true).  The argument causes the deletion of
the old session.  Then proceed with the normal logic for the login
page.  The logged-in session will have the new session id.

Now, this idea doesn't prevent (or attempt to prevent) hijacking
of unexpired sessons due to snooping or extremely lucky guessing
or the user publishing the session ID.  It does prevent tricking a
user into using a pre-determined session ID, which can then be
trivially guessed.

Re: Session fixation idea (Gordon Burditt) wrote:

Quoted text here. Click to load it

This is problem I had already considered. With the current design of the
'session' extension, the function session_start() actually has two

1. If the browser does not sent a cookie, or the sent cookie is not valid
or expired or does not match any of the existing session files, a new
session is created with an empty $_SESSION[] array.

2. If the cookie returned from the browser match an existing session file,
the session is renewed (the same cookie is sent back to the browser).

Unfortunately this design has two drawbacks:

a. Entropy waste and possible security issue. Commonly, programmers call
session_start() in every page of their WEB site. A malicious remote user can
then repeatedly call any one of these pages so forcing the server exhaust
its entropy reserve, and the less secure pseudo-random number generator is
used instead.

b. The programmer needs to add an element to the $_SESSION[] array in
order to check for a valid login session. Typically this element is the
user name $_SESSION['user'] or the primary key that identify this user
$_SESSION['user_pk'] or simply a flag $_SESSION['logged_in'].

In my opinion, two functions should be introduced in place of session_start():
session_new() and session_exists().

session_new() create a new empty session and send the corresponding cookie to
the browser. This function must be called only after the user has successfully
logged-in. The implementation is really simple:

/*. void .*/ function session_new()
    $_SESSION = array();

session_exists() return FALSE if the browser does not sent a valid
session cookie, otherwise this function renew the session calling
session_start(), then return TRUE. This is the function that must be
used in every page of the WEB site (apart the login page). If the session
exists, the code of the page can be executed, otherwise the browser
should be redirected to the login page:

<?php  // some_page.php
if( ! session_exists() ){
    header(" ");

// rest of the page here:
header("Content-Type: text/html; charset=utf-8");
// etc. etc.

Note that the program does not need to check for the existence of the
$_SESSION['user'] or $_SESSION['user_pk'] elements, since if the session
exists it was certainly created by our program at the time of the login.
The implementation of the function session_exists() can be as follow:

/*. boolean .*/ function session_exists()
    $sn = session_name();
    if( ! isset( $_COOKIE[$sn] ) )
        # No cookie from client.
        return FALSE;
    $sv = (string) $_COOKIE[$sn];
    if( preg_match('/^[-,a-zA-Z0-9]+$/', $sv) !== 1 )
        # Not a valid cookie syntax.
        return FALSE;
    $sf = session_save_path() ."/sess_". $sv;
    if( ! file_exists($sf) )
        # This cookie is not a session or session expired.
        return FALSE;
    session_start();  # restore session.
    if( session_id() === $sv )
        # The restored session is that we actually expected.
        return TRUE;
    # Race condition detected: the old session $sv expired
    # in the meanwhile and a new one was created by session_start(),
    # so $_SESSION[] is empty.  Roll-back and return FALSE.
    return FALSE;

Quoted text here. Click to load it

session_id() returns the empty string before session_start(), and the
session value after session_start() has been called, so this function
is not useful to test for an existing session created at login.

Quoted text here. Click to load it

I don't understand this last sentence. Session IDs are always created by
the server. The browser cannont force the server to create an arbitrary
session with an arbitrary value.

/_|_\  Umberto Salsi

Re: Session fixation idea

Quoted text here. Click to load it

But I'm not looking at it BEFORE or AFTER session_start(), I'm
looking at it DURING.  Specifically, when the session save handler
open() routine is called (while session_start() is running).  THEN,
it appears to be usable to tell whether a session is going to be

Quoted text here. Click to load it

The browser usually can make the server create an arbitrary session
with a user-specified *ID*.  This is called session fixation, and
what I'm trying to prevent.  The contents of the $_SESSION variable
may not be specified, or sessions would be completely hopeless for

In the existing setup, pretty much every page calling session_start(),
all that is necessary for creating a session with an ID that I, the
user, specify, is to create a cookie in my browser and make a HTTP
request of the server.  This is really easy with things like CURL.
It gets worse.  With trans_sid set, the user can ask for:

Now, this isn't absolutely horrible security, because there's no session
file for this, so it starts off fresh with no session variables set.
That's why existing pages check for $_SESSION['user'] or something.
They may also need to have that variable anyway:  many pages need to
know which user is logged in to display user-specific information.

The bad part comes when you trick another user into forcing a session day
which you, the bad guy, can guess easily because YOU set it.

Note that for some applications, setting up a session for anyone, no
login required, is desirable behavior for, say, multi-page surveys to
pass information between the pages of the survey.  The login model is
not the only use for sessions, although it is a very common one.

Site Timeline