I've created a login system that uses cookies and stores a session ID in a database, so your login will only work with that particular session ID. I realise this has a few problems:
If you login on another device the session ID changes (no multi logins)
The session ID is really the only thing identifying the user as logged in (I'm not really sure if this is a security risk since the cookie is domain specific)
However I want to retain the persistant login that comes with cookies while still keeping stuff secure.
Effectively I want to know if there is a better way to securely log a user into a website using cookies.
First of all, keeping stuff secure and persistent logins don't go together; you will always compromise security by introducing persistent logins in some way.
Having said that, an article from Charles Miller outlines such a system:
create a (big enough) random key, preferably by using /dev/urandom or openssl_random_pseudo_bytes() and associate it with an account (in database terms: a separate table with the random key as the primary (or unique) index and the account as a foreign key); the key will be the cookie value.
when a non-logged in user presents a cookie, the key and account are looked up and the user is logged in; afterwards, the used key is replaced with a new random key (cookie is updated too).
users who are logged in via cookie alone should be asked for their password again when they access sensitive (account) information.
the user should have an option to log out from all his devices.
It's also a good practice to use a renew the session id using session_regenerate_id() whenever a user is logged in (either via a form or cookie). This prevents someone from launching a session fixation attack against someone else and possibly steal their identity.
An improvement on this design by Barry Jaspen that can also handle identify theft detection can be found here.
Related
Would like to get some peoples views on the following:
Is it good/best practice to query the database for every secure page on a website for a salted and encrypted code check between session / cookie and database value?
or
Is it good/best practice to rely on salted and encrypted sessions and cookies (where both key and value are both salted and encrypted)?
So user id and access level are either picked up out of the database on every secure page or from the encrypted session / cookie value pair.
CONCLUSION
This is what I have chosen to do:
LOGIN
Validate username & md5(password) are in DB and are correct
Start a session
Session store encrypted key & value pairs for: user_id, admin, access_level
Create a ACCESS KEY by joining REMOTE IP & REMOTE USER AGENT & Config encription key, encrypt each, join the strings together and encrypt again
Store this value in both the db and session with the key encrypted
User is now logged in
For page validation:
I will choose two levels of page user access validation:
Low priority secure tasks eg details listings, upload an image, select queries mostly
High priority secure tasks eg delete & update
Low priority page check:
Simply check the existence of encrypted session keys and values for user_id, access_level & existence of access_key
High priority page check:
Query db for session access_key match and re-set session values for user_id, access_level
Additional:
I will add an IP recording feature which will track IPs in the DB for login attempts and if that login fails from that IP over 10 times, the IP gets banned.
Certainly not from cookies. The session cookie should be a completely meaningless pseudo-random blob of gobbledygook. This gobbledygook is the id of a session that is stored server side. This session data can be stored in a database or in the filesystem (which it is by default with PHP sessions). You authenticate the user once with his stored credentials, then you open a session for him and store his user id and whatever else you need in this session. The user is then identified and authenticated by his meaningless session cookie and the data which belongs to it. No more, no less.
There is an argument to be made for occasionally checking whether the data in the session is still in sync with the database though. Say, a user has certain "access levels" and you store this in the session when the user authenticates. Now you change the user's access level in the database; the session will still have an old copy of that data and the user will still be able to access levels he may not be allowed to anymore. It's up to you how often you want to verify this data against the canonical database store.
You can place "sensitive" data in cookies, it is used by a lot of systems e.g. CodeIngnitter.
To minimize the risks make sure that your crypto is implemented correctly.
IF your crypto is ok then there is not much difference between a session cookie plus a db look-up and values stored in the cookie.
In both cases :
Use HTTPS if it is possible.
Use only HTTP_ONLY cookies, so the session cookie does not show up in document.cookie
Remove any "reflection" pages where you might display cookie information, a good example would be phpinfo() pages.
Be sure that you add some parts of the IP address to the mix, and maybe the User-Agent.
If somebody has managed to grab the cookie or session cookie it can't impersonate the user, unless it knows the User-Agent and can send HTTP request from that IP or IP Class.
When a user logs into my site it creates 2 cookies, one with a session ID (that relates to the user ID on the backend) and a remember me cookie that lasts for 3 months.
The remember me cookie is constructed as:
userid:timeout:hash
Where the hash is a HMAC SHA256 hash of userid:timeout to prevent tampering.
If the session ID does not exist (user closes their browser and opens it again so the cookie is gone, or the session ID does not exist in memcached) it looks at the remember cookie and re-generates a new session cookie, providing it has not timed out and the hash is correct.
However I don't see the point of having a session cookie at all, as the session ID just points to a user ID in the backend. I can use the remember me cookie instead to retrieve the current user.
So I am thinking of scrapping the session cookie completely, and would be interested in hearing some thoughts on this. Does this approach sound relatively secure? Could I make it any better?
Thanks in advance!
Yes, it is indeed secure enough for most cases, but why including user specific data in the cookie when you can avoid it? Also, there's a small disadvantage with this:
What happens if an user manages to steal a cookie from another user, you'd have to change the whole way the cookies are generated or that user will always have access, therefore resetting everyone's cookies. Imagine now that it's your cookie that gets stolen...
This is my solution for that: create another row in the user table called 'userhash'. When an user logs in, you generate a random hash without taking any of his input, just random, and store it both in the table and in the cookie. Then you only have to store userhash:timeout in the cookie. You check that against the database to see if it exists, if it does, that's your user. When the user logs out, the cookie and the row in the database gets deleted. For obvious reasons, you'd have to check that the cookie exists before comparing (there will be many empty).
Note: This method would only allow one registered cookie at once, so no laptop + desktop. This is good, since stealing is made more difficult as it only lasts as long as the real user doesn't log in, and bad because it only allows 1 computer. But you see the idea and how you could use this method but having several computers logged in... facebook-like.
PD, it'd be nice if you said how secure your app must be actually...
PD2, in case you haven't think about it yet, there are other more serious security concerns (SSL to say one).
When a user log in and check the "remember me" box, I generate a key (very random numbers on a md5) for it and save on it's cookies. If the user is not logged, my code check for a "remember me key" cookie, if it matches with a user, then he's logged in.
My question is, how do I stop users from coping their remember me key cookie and pass it to their friends? Because if they do that, the person who copied the cookie will be logged in without even knowing the password of the account, then they would access a premium account without buying it.
I can't bind the key to the ip, or else the remember me wouldn't work well, since lots of computers change ips very often. I though about saving the user agent and others browser infos, what do you think?
You can detect the sharing of cookies by regenerating the key for the cookie each time it's used. If someone gives a remember-me cookie to someone else (or it's stolen) and they both use it, then they will both end up with different keys after they use their cookie.
Only allow the most recently generated key for each account. If someone uses a key that doesn't match the database value, then invalidate all sessions associated with the user.
My question is, how do I stop users from coping their remember me key
cookie and pass it to their friends?
Best solution is not to use remember me:
https://www.owasp.org/index.php/Guide_to_Authentication#Remember_Me
If you still want to then you could check the requesters browser and ip but then maybe your have useability issues. Limiting the number of concurrent sessions to 1 per paid user may deter the exploit you are concerned about.
In this age we have different machines, devices and phones, but sometimes we would like to be remembered by our own name.
I have a website where one person should be able to check "remember me" on the device he is currently working on, and having this working on all of your devices in the house.
Currently I was using a remember me function which creates a hashed key, saving it in the cookie, and in the database.
However - when logging in with the same user, but on an other device, the hashed key in the database is overwritten so the remember me function on the first device is down.
I was thinking to ceate a session table to hold the different sessions, (although it might hold different sessions for one user as well)
So Question:
How can I set/generate a unique session key for a device with php.
a browser fingerprint won't do as I use same browsers on different devices.
anyone ideas?
ofcourse I need a secure solution, preventing copying the cookie to another device or changing cookie information (from your user to admin) is important.
For a start having a hash key instead of a username does not add any extra security.
Just use a cookie with the username in it. The password is there for security.
I would do the session table to store all the sessions. Store the user's ID and the session ID in a cookie, that way when the user comes back, you can check to see if they are both in the table. If they are, they don't have to log back in.
The basic idea is to store the session ids from the different devices and tie them to one user. On the database level that means you don't have a "session_id" field in your user table but a separate table with "session_id" and "user_id" columns.
Please think about the security implications of session fixation and session hijacking. For a description of a more secure "remember me" system, read these articles:
http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/
http://jaspan.com/improved_persistent_login_cookie_best_practice
ofcourse I need a secure solution, preventing copying the cookie to another device or changing cookie information (from your user to admin) is important.
Ultimately, this is solving the wrong problem.
The way to prevent this is to:
Use HTTPS everywhere.
Send all cookies over HTTPS, with the secure and httpOnly flags.
That's it. This is related to the problem of client authenticity. There are some techniques that can stop lazy attackers (e.g. user agent), but any of these techniques can be spoofed trivially.
I am making a registration/login system with php. I think I have all the initial login stuff worked out(hashing password with salt, store in db...).
My question is in regard to keeping a user logged in between pages after their initial login. The way I understand it is that one method is to have a table of sessions on your server that stores a random unique id for each user and to store that id in a cookie on the user's computer. This way for each page they load all you do is lookup their session id in your database.
What I don't understand is how is that is secure? Couldn't somebody just sniff the ID and then fake being that user. Someone could even just try guess IDs.
I also read that it is better if the ID changes on each page visit. How does this increase security? It seems it just would decrease the amount of time any ID could be used.
Also how would any of this change with a "Remember Me" feature that would be stored for long time?
The ID you are describing is precisely what the session ID is, except it's handled for you transparently by php (browsers pass along this session ID with the cookie).
The security flaw you are describing is precisely what firesheep takes advantage of. You can prevent the session ID from being sniffed by making sure that all authenticated requests to your site take place over ssl. This not only includes logging in, it also includes any time an authenticated user tries to access a page (which means the browser will be passing along an authenticated session id).
If a user tries to access a page not via SSL, you should ideally redirect them to an SSL page and give them a new session ID, because the old one could have been compromised.
The key to such a system is that you don't randomly generate the key--you generate it using facts about the user, ones that another client wouldn't have knowledge of--like the user's IP address, user-agent, and session id. Then you make the user authenticate using that key and their session id (which is transparently handled by PHP).