Secure session management

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

Post Reply
User avatar
tkstock
Forum Newbie
Posts: 6
Joined: Wed Aug 22, 2007 10:29 pm
Location: Richmond, VA

Secure session management

Post by tkstock »

I'm trying to create a secure session environment. I've come up with the following methodology. Do you see any flaws in it?

Logging in
  • Table: USERTABLE
    Fields
    • USERNAME
      PASSWORD
      (and others)
Function login($username, $password)
  • Select records from USERTABLE where USERNAME = $username
    If record doesn’t exist then
    • Warn user of incorrect login information
      Return to login page
    If record exists then
    • If PASSWORD doesn’t match $password then
      • If LOGIN_ATTEMPTS > 2 (three or more) then
        • Lock account
          Warn user that account is locked and to contact customer service
          Redirect to login page
        Warn user of incorrect login information
        Increment LOGIN_ATTEMPTS by 1
        Return to login page
      If PASSWORD matches $password
      • Create new session (createSession())
        Set LOGIN_ATTEMPTS back to 0
        redirect to proper page
Note about passwords:
•The passwords are stored by hashing the user’s password with a 6 character random string to help prevent dictionary attacks
•When the users account is locked, the word “LOCKED” is placed in their password field. It is impossible for an attacker to provide a password that will hash to the word “LOCKED”, so this effectively locks the account.


Creating new sessions
  • Table: SESSIONS
    Fields
    • USERNAME – primary key
      PASSWORD
      SESSION
      COOKIE
      IP
      TIMESTAMP
Function createSesssion($username, $password)
  • Delete any sessions with username = $username
    Insert new session information:
    • USERNAME = $username
      PASSWORD = $password (sha1 hashed)
      SESSION = session_id() //PHP function
      COOKIE = md5(username + timestamp) // also set on user’s machine
      IP = getenv(‘REMOTE_ADDR’) //user’s current IP address
      TIMESTAMP = time session is created (for timeout purposes)

Checking for valid session (every required page)
Function checkSession()
  • Select all records in SESSION table where:
    • USERNAME = the SESSION username ($_SESSION[“username”])
      PASSWORD = the SESSION password ($_SESSION[“password”])
      SESSION = session_id()
      IP = user’s IP address (getenv(‘REMOTE_ADDR’))
    If no records exist then:
    • destroy any existing session variables
      redirect to login page.
    If a session record exists then:
    • If session record (USERNAME, PASSWORD) doesn't match what is in USERTABLE then
      • delete session information from database
        destroy session variables
        redirect to login page.
      If session record older than 1 hour but less than 5 hours then:
      • delete session information from database
        destroy session variables
        redirect to login page.
        Let user know their session timed out.
      If session record older than 5 hours then
      • Delete session information from database
        Destroy session variables
        Redirect to login page
      If COOKIE exists in database then:
      • If COOKIE doesn’t match user’s cookie then
        • Delete session information from database
          Destroy session variables
          Redirect to login page
    Update session timestamp in database and continue rendering page
BTW, it was reasonably time-consuming getting this to look right - I hope it's understandable!!

Thanks for your help!
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Post by Mordred »

A few comments from a diagonal scan of your text (sorry, don't have time for a serious analysis)

You don't (shouldn't) need to keep username,password and session id in the session
session_regenerate_id() on login/logout
For salting the passwords use a random string of 12 characters, it's twice better than six ;)
Don't lock user accounts like that, a simple dictionary attack will be able mass-lock lots of your users and DDOS your customer service. Captcha after 3rd failed attempt, and lock account by a flag (so the customer service can unlock with a simple click, instead of going through a password reset process). Anyway, I've never liked such protections, maybe temp IP ban after 100+ attempts is okay, but anything lower is too easily abused by evil humans.
Don't, and I mean don't ever display different error messages when a username or a password is not found, or an account is locked.

Your pseudocode is so detailed that you might have as well just posted the code itself, it would have served you better.
User avatar
tkstock
Forum Newbie
Posts: 6
Joined: Wed Aug 22, 2007 10:29 pm
Location: Richmond, VA

Post by tkstock »

Thanks for your response!

You don't (shouldn't) need to keep username,password and session id in the session
This is a secure environment on a shared host. I've heard it was possible for other hosts to generate valid sessions on your site. This is a double check to prevent that. I took some of the advice from here: http://www.devshed.com/c/a/PHP/Creating ... in-Script/
session_regenerate_id() on login/logout
Why not just destroy the session? When they attempt to load a new page, the session will be regenerated automatically, won't it? I'm not sure what regenerating a new session ID will help me security-wise.
For salting the passwords use a random string of 12 characters, it's twice better than six
I was unsure about this. I'm using SHA-1 hash, which is 40 characters. If a user uses a passphrase that is 30 characters long and I add 12 characters onto that and hash it - what happens?
Don't lock user accounts like that, a simple dictionary attack will be able mass-lock lots of your users and DDOS your customer service.
That's great advice - goes to show you can be TOO secure...
Captcha after 3rd failed attempt, and lock account by a flag (so the customer service can unlock with a simple click, instead of going through a password reset process).
Another great idea. I'm already using captcha on account creation, so that will work fine!
Don't, and I mean don't ever display different error messages when a username or a password is not found, or an account is locked.
Yep, already aware of that one. ;)
Your pseudocode is so detailed that you might have as well just posted the code itself, it would have served you better.
The code is not in development yet, I'm just trying to draw up the framework for session management.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Post by Mordred »

(answers inlined in bold, too lazy now for proper formatting, sorry about that)
tkstock wrote:Thanks for your response!

You don't (shouldn't) need to keep username,password and session id in the session
This is a secure environment on a shared host. I've heard it was possible for other hosts to generate valid sessions on your site. This is a double check to prevent that. I took some of the advice from here: http://www.devshed.com/c/a/PHP/Creating ... in-Script/

You will notice, that I said nothing about IPs, which is what that article advises about. Using IPs for double-check should be optional for the user though, as some ISPs may change IPs between requests, or many user may be behind the same proxy and etc. problems.
There is something I've omitted to comment here - are you or are you not creating sessions stored in the database? You are not, but you duplicate some of its functionality, and you try to solve problems which will not be there if you did. So do it ;)

session_regenerate_id() on login/logout
Why not just destroy the session? When they attempt to load a new page, the session will be regenerated automatically, won't it? I'm not sure what regenerating a new session ID will help me security-wise.

You sometimes want to keep data in it, so you can't destroy it. Read about session fixation.
For salting the passwords use a random string of 12 characters, it's twice better than six
I was unsure about this. I'm using SHA-1 hash, which is 40 characters. If a user uses a passphrase that is 30 characters long and I add 12 characters onto that and hash it - what happens?

40 charaters is the OUTPUT of the function. The input can be anything. If, on the other hand a user has a 2-3 character password, 8-9 characters are still within the reach of a modern bruteforce.
User avatar
tkstock
Forum Newbie
Posts: 6
Joined: Wed Aug 22, 2007 10:29 pm
Location: Richmond, VA

Post by tkstock »

are you or are you not creating sessions stored in the database? You are not, but you duplicate some of its functionality, and you try to solve problems which will not be there if you did.
I'm not sure what you mean by this. Is there a provided way to create sessions in a database? I plan to store some session information (listed in the SESSION table in my first post on this thread), and will check this data each page that requires a secure session and cross-check with the cookie, but that is the extent of my use of the database to manage sessions.

Is there a standard way of storing sessions in a MySQL database?

Thanks once again for your responses! I just started coding PHP a few weeks go (and the last couple of days are my first foray into session management), but it's been a hoot!
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Post by Mordred »

session_set_save_handler()
Alternatively, session_save_path() to see what happens on your host, or set a new path and just store them the old fashioned way.
Post Reply