Reputation: 29664
A few years ago I developed a web app for which we wanted to make sure the users weren't sharing credentials.
One of the things we decided to to, was only allow the user to be logged in from one computer at a time. The way I did this, was to have a little iframe ping the server every N seconds; as long as the server had a heartbeat for a particular user (from a particular IP), that user was not allowed to log in from any other IP.
The solution, although approved by my manger, always seemed hacky to me. Also, it seems like it would be easy to circumvent.
Is there a good way to make sure a web app user only logs in once? To be honest, I never understood why management even wanted this feature. Does it make sense to enforce this on distributed apps?
Upvotes: 9
Views: 2313
Reputation: 5100
Having worked on a 'feature' like this be warned - this is an elephant-trap of edge cases where you end up thinking you have it nailed and then you or someone else says "but what if someone did X?" and you realise that you have to add another layer of complexity.
For example:
and so on...
Basically there are a range of more or less hacky solutions none of which are foolproof and all of which are going to be hard to maintain. Usually the real aim of the client is some other legitimate security goal such as 'stop users sharing accounts'.
The best idea is to find out what the underlying goal is and find a way of meeting that. And I'm afraid that involves negotiotion diplomacy and other such 'soft skills' rather than embarking on a technical wild goose chase..,
Upvotes: 4
Reputation: 13761
I would turn the problem around, and allow the last login at the expense of any earlier login, so whenever a user logs on, terminate any other login sessions he may have.
This is much eaiser it implement, and you end up knowing where you are.
Upvotes: 4
Reputation: 4300
I just had this problem.
We were building a Drupal site that contained a Flex app (built by the client), and he wanted the following:
He tested the crap out of every solution, and in the end, this is what we did:
This solutions satisfied our client's rigorous testing (2 computers in his house..he kept us up for hours finding little nooks and crannies in the code before we came to this solution)
Upvotes: 1
Reputation: 18061
Looking at just IP can be unreliable. IIRC there are some styles of proxy that farm outgoing requests randomly over multiple IP addresses. Depending on the scope of your application, this may or may not affect you. Other proxies will show heaps of traffic from a single IP.
Last login time can also be an issue. Consider cookie based authentication where the authenticate cookies isn't persistent (a good thing). If the browser crashes or is closed, the user must log back in, but can't until the timeout expires. If the app is for trading stocks, 20 minutes of not working costs money and is probably unacceptable.
Usually smart firewalls / routers can be purchased that do a better job than either you or I can do as a one-off. They also help prevent replay attacks, cookie stealing, etc, and can be configured to run alongside standard mechanisms in your web platform of choice.
Upvotes: 3
Reputation: 175653
I've implemented this by maintaining a hashtable of currently logged in users, the key was the username, the value was their last activity time.
When logging in, you just check this hashtable for the key, and if it exists, reject the login.
When the user does anything, you update the hashtable with the time (This is easy if you make it part of the core page framework).
If the time in the hashtable is greater than 20 minutes of inactivity, you remove them. You can do this every time the hashtable is checked, so even if you only had one user, and the tried to login several hours later, during that initial check, it would remove them from the hashtable for being idle.
Some examples in C# (Untested):
public Dictionary<String,DateTime> UserDictionary
{
get
{
if (HttpContext.Current.Cache["UserDictionary"] != null)
{
return HttpContext.Current.Cache["UserDictionary"] as Dictionary<String,DateTime>;
}
return new Dictionary<String,DateTime>();
}
set
{
HttpContext.Current.Cache["UserDictionary"] = value;
}
}
public bool IsUserAlreadyLoggedIn(string userName)
{
removeIdleUsers();
return UserDictionary.ContainsKey(userName);
}
public void UpdateUser(string userName)
{
UserDictionary[userName] = DateTime.Now;
removeIdleUsers();
}
private void removeIdleUsers()
{
for (int i = 0; i < UserDictionary.Length; i++)
{
if (user[i].Value < DateTime.Now.AddMinutes(-20))
user.RemoveAt(i);
}
}
Upvotes: 12
Reputation: 2331
I've never found a standard solution to this problem. In one of my apps I used a combination of Javascript + Java to ensure that a user could be logged only once from a specified IP (actually it was a session ID), but in the worst case of functioning, for a timeout (set to 2 minutes) the account was not available. I don't why there is not a common way to do it.
Upvotes: 1
Reputation: 5150
In a highly secure application, you may be required to do such. What you can do is keep a login count incrementing that for the user that logs in and the IP address. The count should never be 2. If it is then you log the other IP out and whomever it is logged into that IP gets thrown out. That wont prevent user-1 from giving his credentials to user-2, it will just make it frustrating for user-1 to do his work if user-2 logs in somewhere else at the same time.
Upvotes: 1