views:

765

answers:

7

Hi,

I'm developing a login and authentication system for a new PHP site and have been reading up on the various attacks and vulnerabilities. However, it's a bit confusing, so I want to check that my approach makes sense.

I plan on storing the following data:

  • In the session: user-id, hashed + salted HTTP_USER_AGENT

  • In the cookie and in the database: random token, hashed + salted identifier

On every page, I plan on doing the following:

  1. If a session exists, authenticate using that. Check that the HTTP_USER_AGENT matches the one in the stored session.

  2. If no session exists, use the cookie to authenticate. Check that the token and identifier in the cookie match those in the database.

  3. If the cookie is invalid or doesn't exist, ask user to login.

Are there any obvious flaws in this? As long as I set a timeout in the cookie, I should be fairly safe, right? Is there anything I'm missing?

Many thanks in advance.

+2  A: 

there shouldn't be. the php session is stored on your server not the user. php simpily leaves a session cookie pointing to it. if you're not on shared hosting the session doesn't even have to be hashed.

joe

Joe Simpson
+7  A: 

A few random thoughts :

  1. What if I steal the cookie of one of your users (using an XSS attack by injecting some JS code in your website) ? I will then fall in case 2. and thus be able to log in. IMHO, if you want a really secure authentication, do not use "remember me"-type cookies to store user credentials.
  2. If you do store the credentials in a cookie, please don't store the password in clear.
  3. Checking for the HTTP_USER_AGENT is a good first step to prevent session hijacking, but maybe you could combine it with the IP address ? It is far more difficult to be on the same host than your target than to simply use the same browser.

But in any case, thanks for taking the time of thinking about a good authentication scheme. A lot of PHP developers don't.

EDIT: for the record, let me clarify a point here : there are two cookies in this discusion. One being set automatically by PHP to propagate the session ID (sometimes, we see websites putting it in the URL, eg www.example.com/page.php?sessionId=[...]), and the second one created by you in order to store the user credentials and authenticate him when the session is lost. The XSS attack applies to both, ie an attacker could either steal the session cookie and hijack the session (which has a limited lifetime), or steal the credentials cookie and authenticate later.

Wookai
Checking a combination of the user agent and the first 3 octets of the IP is fairly good. I wrote an article on the subject at http://www.jqueryin.com/2009/11/20/php-secure-sessions/.
cballou
Indeed, user agent + ip address is more than enough. But why dropping the last octet ? IMO, it is the most significant part of the IP address, as my neighbor may be in the same ISP subnet and thus have the same first three octets ? Great article btw, I personnaly use two salts : one fixed in a configuration file, and one randomly generated on user creation.
Wookai
Storing the cookie in the database is a HORRILBE idea. It means that if an attacker had a sql injection vulnerability he could immediately gain access without having to crack a hashed password.
Rook
In point 2., by storing the cookie I did not mean in the databse, but I meant storing the credentials in a cookie. I'll edit my answer to make things clear.
Wookai
User agent can be spoofed. You have no idea what you are talking about. You have never hijacked a session, even if it was just your local system. Don't spread lies!
Rook
Where did I say that a user agent cannot be spoofed? Instead of insulting others, post comments that are basically useless and promote some random project (that I suspect you may have written), why don't you link to something like http://www.owasp.org that would have been way more useful to the OP ?
Wookai
Btw, if an attacker can perform an SQL injection and retreive something from the DB (your argument about storing cookies in your answer), the cookies are the least of his concerns (and your problems)...
Wookai
+1  A: 

Depends upon how safe you want to be..

Cross-site vulnerabilities: Lets say another site directs the browser to submit a form to your site that does something like posting spam (or worse) if that user is already logged on the form submission will work. You'll need to check referer and a generated hidden formID for each form to protect against this fully.

Second: If you have high to medium traffic, sessionIDs can be repeated or even guessed, I'd check against a second hand generated ID that is stored in the users cookies.

MindStalker
+1  A: 

The scheme seems needlessly complex in a few ways, in that the added complexity doesn't gain you anything in functionality or security.

  1. Any data that's sent by the browser (e.g. cookies, User-agent) is spoofable. Checking User-agent will only help when the attacker is behind the same NAT as the spoofed user and the attacker is using a different browser but doesn't think to change the User-agent.
  2. Sessions store the session id client-side using either cookies or a URL query parameter. If you want to extend the life of a session, use session_set_cookie_params to keep the session cookie around longer.

The User-agent isn't secret data, so hashing it is unecessary.

The attacks that session cookie+checking remote IP won't catch are:

  1. the attacker is behind the same NAT as the user
  2. Blind injection attacks, where the attacker spoofs the user's IP. Despite being write-only, these can still do some damage.
  3. attacks that make use of the user's own browser, such as cross-site request forgery (CSRF).

2) could be prevented if you can work out a way of sending a challenge to the user's browser, which must be responded to before completing the request, but this is tricky when you didn't write the client. With AJAX it can be done. 3) (as noted by MindStalker) can be prevented by checking the Referer header, which works because CSRF attacks don't have the ability to affect arbitrary headers, and XMLHttpRequest shouldn't allow the Referer header to be set (as per the W3C standard, though implementations might not be compliant). With iframes, it might be possible to get around a Referer check. Also, the Referer header might be blocked client-side.

outis
+2  A: 

Storing the cookie in the database is a HORRILBE idea. It means that if an attacker had a sql injection vulnerability he could immediately gain access without having to crack a hashed password.

Speaking of which you need to use sha256 for passwords, if you use md5() you are technically vulnerable to attack and you could be issued a CVE number.

NEVER generate your own Session id's, use session_start() and the $_SESSION super global.

This is a secure way to redirect people. If you don't die after header() the rest of the php code is STILL EXECUTED even though its not displayed by normal browsers (hackers still see it :)

header("location: index.php");
die();

To be honest if security confuses you, don't write security systems. People have written more than 1,000 login systems for PHP alone and the majority are vulnerable. This project has a secure authentication system: http://code.google.com/p/michael-the-messenger/downloads/list

Rook
On your first point - I don't mean that it will take the user's cookie and store it in the database; that would be silly! I mean that a token and an identifier are created and then one copy is put in the cookie and the other in the database. No data from the cookie passes into the database, so it can't be injected that way.Thanks for the thing about header(), I never knew that.
Philip Morton
Exactly, that is a massive mistake. By storing the cookie value in the database you make sql injection against your system much more valuable. You must assume that your system will broken into, and you must have a plan of action do deal with the breach. This is why passwords are stored as hashes. Further more php comes with a session handler, if you re-invent the wheal i assure you that it will be less secure.
Rook
Don't worry - I have no plans to create my own session handler!
Philip Morton
A: 

Most sites just use the PHP session; the session data ($_SESSION) is in a file on your server. All that's sent to the browser is a session ID. Be sure to regenerate the session each request (session_regenerate_id). You don't need to be sending two cookies or anything.

This is less vulnerable to session hijacking as every request is a new ID, so an old one intercepted by an attacker is useless.

The best solution, obviously, would be to use SSL throughout the entire session.

Leafy
A: 

IMHO it is also important that the session informations are changed after a success login. To save the session information in a database isn't save because of injections.

DrDol