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.
Related
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.
On Monday, I thought I had solved the session hijacking security issue by setting the session as the user IP, until I logged in. I had two users with the same IP (myself and a test user) and it kept switching between the two. Is there a way to prevent this and allow two users with the same IP register on my site?
Thanks in advance,
Terry.
You may have been reading advice about storing the user's IP in a table along with the session id (not in place of). You'd then check to make sure they're coming from the same IP on subsequent requests, otherwise, force them to login again. This method has problems as well a user's ip can change as often as every ten minutes depending on their ISP!
Use the session id provided by PHP as it's unique and difficult to guess. Require it to be read from a cookie and never from the URL.
SSL the entire site if it is a concern and apply a short cookie time out. The ssl will encrypt the cookie and transmission so it can not be sniffed off the wire. A short time to live will make the cookie useless soon after it has been taken from the "logged in" computer if they have direct access to the system. So in short get a security cert and go on as normal with a normal php session.
I take it you're looking for the user's information in the MySQL database, using their IP? That is wrong. The only way to be truely unique is with a primary key field.
Either store the primary key as the session and pull their data, or store relevant information in the session and only pull anything else when it is needed.
I'm trying to find a solution for securing remember me functionality. Cookies will be used.
function enc($string) {
$slat = "%32!#x"; //can be auto-generated
$hash = sha1(md5($slat.$string)).md5($string).sha1(md5(md5($string)));
return $hash
}
echo enc("password"); //store user's password in db
I started thinking "What if a cracker steals my member's cookie!" In this case, the Hacker will have access until hacker OR original user logs out. There will be no evidence (other than last login) that account is hacked. In this case, hacker will always have access every time the original user logs in with "Remember Me" until original user changes the password.
Assuming, hacker couldn't have the password.
Solutions (how to prevent hacker get an access if he/she stole the user's cookie):
1. In db user table, i put "Token" field which will carry salt value.
2. Everytime user logs in via the form or via the cookie (auto-login), update the token with auto generated value. Also, update the password field with the new hash.
3. Now set the cookie with the new values.
4. The previous cookie is not valid anymore.
5. Assume hacker got access, his access will be temporary until original user access his account and won't be able to do major changes such as changing password/email.
Hacker and User will share step 2! Everytime hacker logs in, user is kicked out from access due to multiple account access and vice versa. This will indicate account being stolen, so it won't be so long till the original user simply go and change the password.
This not a %100 solution, but reduces chances of risks and warns the user that there is another person using the same account.
I'm trying to keeping it as simple as it can.
Is the solution good enough? Are there major flaws in the solution?
What you are looking for is session hijacking prevention. This link will explain to you what needs to be done. There is no need to parrot in here what is already said and done. This is a description of the attack with illustrations from The Open Web Application Security Project.
I'm going to try to keep this short, because it could easily get too long.
An option is to include some more identifying data within the cookie, such as the user's IP address (which obviously may change) or their browser's user-agent (which really shouldn't change). You can include other things if you want, I'm just using these for now so I can keep it shorter :)
encrypt ( (user-agent and/or ip) and hash(token) )
Would give you a decryptable string which you could substr() a known length off the end of to get your token back, and compare the other parts to the user's current data.
Alternatively,
hash(user-agent and/or ip) + hash(token)
Would allow you to substr the token off the end immediately then just hash the current user-agent and/or IP and compare it to the cookie value.
Obviously this is more about how to narrow down the chance the hacker can use the stolen token. If the original user was using Firefox 3.5 with a certain user-agent, the hacker would have to use that too. If it came from a certain IP, that would need bypassing, and so on.
This is how I'm building a login system:
Login:
Check username and password supplied by user with the database.
If username and password is correct, store only user ID in session, something like:
$_SESSION['userid']=$userid;
If User has checked the option to stay logged in, then set 2 cookies, 1 with userID and other hashed string.
To check if user is logged in:
Check if Session exists, the user is logged. is it ok?
If session does not exist, check if both cookies, userID and hashed string exist.
If Both cookies exist, validate them.
As the Session is stored in the server, is it secure to store only userID ? Can a user pretend to be other user and store his userID in the session and log in as him?
Thanks.
Yes, this method is very insecure. I can sniff traffic, intercept your cookies, and your system will accept me as an authenticated user. You are making the assumption that if you get a cookie with a userid and the hashed string, then that user is the same person that originally authenticated to create the cookie. That is a poor assumption, because cookies travel in plain text (unless you encrypt them), so as long as I can grab a cookie, I can pretend be whoever sent that cookie, and your system doesn't know any better.
Edit:
If you are going to use unencrypted cookies, why not just store the session_id in a database table? That way, at least someone that gets hold of a cookie won't have a valid username. Create a sessions table, and when someone successfully authenticates add a row with their user_id and the session_id. Each time a page is loaded, check to see if the session_id in the cookie matches a row in the sessions table. If yes, you can assume the associated user_id is the authenticated user. This approach is just as secure as the one you suggested (i.e. not very), but it's less complex and doesn't give away valid usernames.
Yes it's possible and very extended, this kind of attacks are called Session fixation and in your system (as David said) anyone who sniff your traffic, or have access to the user's drive and steal his cookies, may supplant a logged user.
The best protection is, of course, SSL, but if you can't use it in your website there are other things that can prevent (but not fully protect against) this attacks:
Save info about the user in the server-side when he login, good candidates for this are the IP and the user agent, but any other data that don't change in the entire session can be valid.
You can regenerate the session ID in every request, with this if the session ID is leaked the attacker must use it before the real user do any other request, but beware because every time the session ID is regenerated (in PHP at least) the user's session data is rewited, so this can be expensive if you have a lot of users or if you save many data of every user (this means that, if you're saving the session data in a file, the file will be deleted, created, and writed again).
Well, right now I can only think in these two, it's not much but at least you will put an extra complication to the attackers.
One more thing, don't trust the user's cookies, they can be changed by the user (or the attacker) at any time, treat it like any other user input.
PD.: Sorry for my horrible english, I'm truly trying to improve it ^_^
you could add an ip that the user id should belong to (in your database), that adds a little extra security - it might not always be the best solution
Yes it is ok to check if the session exists and also check that the user id is greater than zero.
The 'remember me' function is subject to sniffing as it's not over ssl, however that is how 'remember me' functionality is done.
Assuming this is happening via SSL, my biggest concern is your first step:
Check username and password supplied by user with the database.
You should be hashing passwords, and comparing the hash of the user-supplied password against the previously hashed password stored in your database.
You also don't have to worry about storing only the user ID in the session array; the session is stored server-side and is as secure as the rest of your server.
One potential problem is that everything is being stored in cookies. If someone somehow manages to get their hands on the Session ID, then they've also got the username and hashed string.
Chris Shiflett suggests creating some kind of fingerprint from the User-Agent string, or some other regular header, and storing it in a GET variable.
One way to bump up security is to have everything sent over SSL. Any time any kind of potential information is sent or received (such as the Session ID in a cookie), make it encrypted - not just the login form.
It is mostly correct but I don't agree with the cookie-option. This way if someone gets the two cookies can move them to a different computer and still use them.
The "remain logged in" function should be restricted to that computer. A possible solution is that if the user wishes to remain logged in you set the lifetime of the session to 1 week or so. Also you have to store the user's IP address, User-Agent and possibly X-FORWARDED-FOR header, and check them on every pageload against the stored values.
In most of past questions on SO about security in 'Remember me' feature in login systems, Persistent Login Cookie Best Practice is suggested.
If I understand correctly, this approach goes like this:
If user checks to remember the password, the cookie consisting the username, followed by a separator character, and some large random number should be set. For example:
$_COOKIE["login"]; ="adam:8794383bb07608636dab808df6c9e29c"
store username and hash string in database.
if both cookies exist, and are mapped to each other in db, the login is accepted.
After authentication, remove the random number from cookie, generate a new number and store in cookie. (Do I need to update this newely generated hashed string in the database as well?)
When user is logged out, current cookie number is also invalidated.(Should I remove username and the random string from the database?)
Is it all?
As in my previous question, I was told that if someone can access the cookie, they can easily authenticate. So how does this approach solves that problem?
Many thanks.
Actually, here is what I would recommend. Sample DB Schema
Users:
user_id
username
autologin_hash
autologin_expire
Process:
User clicks remember me
Server assigns a unique token and stores it in the database and sends it as a cookie.
Server also assigns a fixed expiration date in the autologin_expire field
Check to see if the user's cookie equals the one stored with their account on the server AND it has not expired
All is good...login, delete the hash, regenerate it, and update the expiration date for the next login
You never, ever want to store usernames or passwords in cookies as they are vulnerable to theft if you are not using SSL. Using a unique hash and clearing it on each login solves these problems: 1) it prevents auth details leakage, 2) it makes a auto login valid only once (cookie cannot be stolen and used again), 3) it enforce a hard expiration date server side which helps prevent abuse, 4) and the long unique ID is hard to impossible to guess so hackers would have to actually steal the cookie to gain access.
Edit: If you want even more security, make a note to clear out the hash if the user changes their password. You don't want valid auto login hashes floating around if the user changed their password out of fear their password was revealed.
4). You do need to update the hash, otherwise the person won't be automatically logged in again.
5). You should for clean up, but if it is invalidated, your code should not process it as valid.
As for stopping other people from loggin in with that cookie, you could do...
User agent must be perfect match as well.
Once logged in, their account is marked remembered login. When it comes to changing passwords, making payments, you request their password again.