views:

434

answers:

7

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?

+1  A: 

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.

Optimal Solutions
+1  A: 

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.

Enreeco
+8  A: 

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);
        }
}
FlySwat
Why was this downvoted? Its the only sane way to do it.
FlySwat
No idea. Upvoting.
ceejayoz
I like your solution. In fact, inactivity (and knowing when to let the user back in), was one of the things that gave me the most trouble. I like the idea of making the "user activity" check part of the basic functionality of every page.
Esteban Araya
@Jonathan: I don't think it's the ONLY sane way to do it :p. Great answer though!
Esteban Araya
Got a better idea Estaban? This is a purely server solution, and works on the account, not on IP's or session ID's
FlySwat
+3  A: 

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.

Robert Paulson
+1  A: 

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:

  1. transparent login from Drupal<->Flex (bleh!)
  2. no concurrent logins!!

He tested the crap out of every solution, and in the end, this is what we did:

  • We passed along the Session ID through every URL.
  • When the user logged-in, we established an timestamp-IP-SessionID-Username footprint
  • Every page, the DB was pinged, and if the same user was found at the same IP w/a different SessionID, they older user was booted

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)

Pete Karl II
+3  A: 

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.

AJ
+3  A: 

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:

  • what if a user opens a new tab with a copy of the session?
  • what if the user opens a new browser window?
  • what if the user logs in and their browser crashes?

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..,

domgblackwell