Session fixation idea

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Gordon Burditt

    Session fixation idea

    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,
    or
    (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_sessi on_fixation_att empt
    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_sessi on_fixation_att empt is set, call
    session_regener ate_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.

  • Umberto Salsi

    #2
    Re: Session fixation idea

    gordonb.ka1po@b urditt.org (Gordon Burditt) wrote:
    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).
    This is problem I had already considered. With the current design of the
    'session' extension, the function session_start() actually has two
    roles:

    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_destroy ();
    $_SESSION = array();
    session_start() ;
    }

    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("http://www.mysite.xxx/login.php");
    exit;
    }

    // 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_pa th() ."/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.
    session_destroy ();
    return FALSE;
    }
    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.
    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.
    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,
    or
    (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_sessi on_fixation_att empt
    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_sessi on_fixation_att empt is set, call
    session_regener ate_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.
    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.

    Regards,
    ___
    /_|_\ Umberto Salsi
    \/_\/ www.icosaedro.it

    Comment

    • Gordon Burditt

      #3
      Re: Session fixation idea

      >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.
      >
      >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.
      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
      created.
      >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,
      >or
      >(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_sessi on_fixation_att empt
      >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_sessi on_fixation_att empt is set, call
      >session_regene rate_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.
      >
      >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.
      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
      security.

      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.

      Comment

      Working...