The dreaded 'keep me logged in' and session checking The dreaded 'keep me logged in' and session checking mysql mysql

The dreaded 'keep me logged in' and session checking


You cannot hijack the contents of a session. If you're the man in the middle, then you can act as the user by using the same session ID. This problem however is still valid for any kind of session.

If you want to prevent the man in the middle attack, you should use HTTPS.

Using HTTPS guarantees that:

  • your sessions are not fake, because there is practically no way an attacker can steal someone's session ID and use it as their own
  • your cookies are not fake, for the same reason
  • your log in method is secure, because everything is sent and received encrypted, which would make it practically impossible for an attacker to either see, modify or intercept the data sent and received from the server

The main disadvantages of HTTPS are:

  • you will need to purchase a certificate
  • encryption will add some overhead, both in network traffic and in resource usage
  • data sent via HTTPS will not be cached


Very nicely illustrated question!

The thing to note about PHP's session support, is that it too relies on cookies. PHP automatically sets a cookie with a session-ID that PHP assigns. It then automatically reads that cookie and loads the session.

Now, cookies can be hijacked (see Firesheep for example) by "man in the middle attacks". The attacker simply copies another person's cookies (which are sent along with every request), and adds them to his own browser. And since sessions are identified via cookies, you can hijack a session in this way (the attacker will simply be use the site with the very same session as the user). Or you can hijack the "remember me" cookie for later use.

The only real safeguard against such hijackings is to have an encrypted connection, i.e. "https" connection. Then, the cookie data sent back and forth will be scrambled for every request, so someone else can't copy its actual content.

Otherwise, you've got the right idea for how to match a persistent-login cookie to a user (i.e. hash in the cookie must match that in the database). For the session checking, you don't need any hashing, as the session data itself never leaves the server. So there you can just set $_SESSION['logged_in_user_id'] to the UID - or to false/null, if they aren't logged in. That is, if the cookie checks out, or the username/password checks out, then set the user id in the session as a simple int.

As for the "manual" log in, where the user sends his/her username and password, you're again exposed to man in the middle attack. As before, it's simply a matter of intercepting the data being sent to the server. Unless you use an encrypted connection, the cookies and the username/password (and everything else) will all be cleartext and plainly readable to an attacker.

Minor points:
For the hashing algorithm, I'd go with sha1 rather than the older md5. And for the username/password in the database, it's never a good idea to store the password as cleartext in the database. Should your database get breached, the attackers could just read everything there regardless of how securely your server otherwise communicates with your users.

You probably know all this already, but just in case: For the users table, store the username as cleartext, but also generate a salt (say, hash the current time plus a random - but constant! - string you define, and hash it repeatedly several times). Store the salt in the database, and use it when hashing the password. And again, hash it repeatedly many times. Finally, store the hashed password in the DB. You won't be able to send people their passwords, because you don't know either (you just know the hash), but you can implement a "reset password" option, which generates a new salt, and a random string, which you then hash like any other password. Just remember to keep the new un-hashed password around long enough to send to the user. The reason you want to hash it several times, is that it makes it makes it unfeasible to use a rainbow table to "reverse" the hashing, even if you know the salt(s). If you just hashed a password once, with md5 and no salt, it'd be trivial to look up the hash and see what the unhashed password is (unless your users are all smart enough to use really long, random passwords which aren't found in any rainbow tables).

Long story short: The session, cookies, and username/password are all vulnerable to the same kind of attack. The most practical safeguard against it is using SSL (since, as you note, IP addresses change).

Also see the other answers. The more info the better :)


how should I check my sessions are not fake?

By setting a variable on creation.

session_start();if(empty($_SESSION) || !isset($_SESSION['notfake']){   $_SESSION = array();   session_regenerate_id();   $_SESSION['notfake']=1;}

how should I check my cookies are not fake?

By checking them in the database. Have a reasonably long identifier. Not much else you can do, besides blocking IPs that have 'to much' non-valid cookies in a short time.

would my log in method be secure enough after the checking is in place?

Depends, you do use HTTPS for everything? Also, set the session-cookie to https-only.

is there anything important I have left out?

For important actions (changing password/email/etc.), require the user to resupply their password. Also, don't use a plain hash, add some fixed string (= salt) to the values prior to hashing to avoid the direct use of rainbow tables.