views:

348

answers:

6

I have been simply writing 2 cookies, 1 containing the user ID, and the 2nd containing 1/2 the SH1 hash of the password (salted). The way it works is self-evident.

I realized that I wasnt doing this in the most secure way. Whats a better way of doing this? Preferably using a single authentication cookie.

Also, is there a point to using "hard to calculate hashes"? By that I mean, using bcrypt, or hashing each item 10,000 times with whirlpool, to make it a (relatively) slow hash function (200 ms vs less than 1 ms just plain SHA1)? I mean if someone breaches your DB and gets the hashes.... what is there left to protect, since all your data is in the same DB (Unless you have some sort of a de-centralized setup, which I dont).

+8  A: 

use Sessions. Store the session id in the cookie, and store the state of the user on the server side (loggedIn, userId, IP).

To clarify what you need to store in the session array:

  • loggedIn: A boolean variable about whether the user is logged in or not. You reuse the same cookie for multiple sessions, so you remember the users username next time they come to your site, etc.
  • userId: The uniqe id of the user in the database. Use this to get more information on the user, like username, email etc. This too can be kept in the session array after the user logs out.
  • IP: To prevent someone from stealing the session id and using it, you store the IP of the user as well. This is optional, as sometimes you want to allow the user to roam (eg, stackoverflow allows me to move about with my laptop without logging me out when the IP changes).
  • lastPing: The timestamp the user was last seen. This can be used instead of the cookie expiration date. If you also store the lifetime of the session, then you can log the user out due to inactivity. This means that the session id cookie can be stored on the users computer for a very long time.

When the user logs out or is logged out due to inactivity, you simply set loggedIn to false. When the user logs in with the right username and password you set loggedIn to true and update the other fields (userId, IP, lifetime). When the user loads a page, you check the lastPing against the current time and the lifetime, and either update lastPing or logout the user.

The session data can either be stored in the filesystem or in a database. If stored in a database, then userId is either a foreign key to the user record, or all the data can be put in the user record.

Hashing

rehashing a value several times is not a good idea, because you reduce the security. Instead use salt, combining a static salt (name of the page for example) and the username of the user, together with the password. A hash that takes a long time isn't better than a fast hash, a hash that results in a large digest is better than a hash that results in a short digest (due to brute force). Using SHA1 should be good enough for a normal site (IE, not a bank or a secret military organization).

Marius
That seems like it would cause a lot of overhead storing these, especially for "remember my login" function, with a large user base. Not to mention another DB call.....
Yegor
If a single DB query is bringing you're application to it's knees, then you've got a problem I'm sure several here would like to have.
Billy ONeal
Currently I make 0 DB calls (except during login, or when something changes) for logged in users. I wanted it to remain that way...
Yegor
I dont understand how re-hasing increases risk of collisions. I thought SHA functions are collison-proof.
Yegor
+1  A: 

If you want to implement a "remember me" functionality for your site, storing the userid in a cookie is acceptable.

You shouldn't store the password in the user's cookie. If they want to, they can save this in their browser.

adam
+1  A: 

Ask yourself how strict your security requirements are to determine how hard this will be or not. I use Zend_Auth & Zend_Acl to handle authentication/authorization in my php apps. If you are currently using a framework you might search it for recommended best practices. Even if you aren't using a framework you could search other sites/applications to get a feel for it. There is more to it than that though when it comes to using https for login/entire & entire logged in sessions, http only cookies, etc.

Eric Butera
+1  A: 

There's several ways you can do this. I'm not going to go too in depth as there's loads of literature on the subject.

  • First of all, it's better to use something like SHA256 to reduce the off-chance of collisions.
  • You should also use salting, or dual-salting with one variable salt (such as the IP -- to prevent stealing sessions). A cookie string would look like YOURSALT123.123.123.123USERNAMEPASSWORD before being hashed.
  • Use sessions in conjunction with your own cookies.
  • Depending on your security requirements, use SSL.

It all depends on how secure you need to be. A community website about lolcats has different security requirements when compared to a banking website.

David Titarenco
+2  A: 

Currently the unique token for identifying a user is their username + 1/2 of the salted password hash. This token is static, meaning it will stay the same on each request until the user changes their password. This means if I want to impersonate a user in the system I only need to capture/intercept the token once. (Unless you're introducing entropy into the token during the creation of the hash stored in the cookie). Since most users rarely change passwords an attacker will have what amounts to a non-expiring token to access a user account.

A better solution is to use PHP's session mechanism and calling session_regenerate_id on every request to constantly update the token. Doing so makes session hijacking nearly impossible especially over an SSL connection with an IP address/range restriction.

Currently I make 0 DB calls (except during login, or when something changes) for logged in users. I wanted it to remain that way... – Yegor 7 mins ago

PHP session data is stored in the filesystem by default so you won't be making additional DB calls by using the built-in session mechanism.

Also, is there a point to using "hard to calculate hashes"? By that I mean, using bcrypt, or hashing each item 10,000 times with whirlpool, to make it a (relatively) slow hash function (200 ms vs less than 1 ms just plain SHA1)?

Hashing a string 10,000 times does not make it 10,000 times more secure than hashing it once. Hash once with a good one-way encryption like SHA1 or Whirlpool.

I mean if someone breaches your DB and gets the hashes.... what is there left to protect, since all your data is in the same DB (Unless you have some sort of a de-centralized setup, which I dont).

Salting password hashes protects them from a rainbow table attacks. Currently, it's very difficult if not impossible to crack salted passwords with a rainbow table.

pygorex1
PHP sessions will not store long term data... like "remembered" logins. Still need to store them in mysql. Hashing 10,000 doesnt make a stronger hash, it just makes the computational effort required to brute force it tremendous.
Yegor
A: 

The answers here are more complicated than they need to be.

You only need to do two things when the user signs in:

  1. Store the user id in a cookie and/or session.

  2. Create a long random string and store that in the cookie/session as well and store it in the database for that user.

Then, each time a page is loaded, check to make sure the cookie/session user id and string matches the stored string for the user id.

Citizen