Question

I want to prevent multiple logins in a php application.

First, I create login status (active, notactive) in a user table.

When user A logs in the user status will be set to 'active', and if the user logs out the status will set to 'notactive'. When another client trys to login using the same user acount, I check the user table. If the user is still active the error login will be sent to the user.

The problem occurred, if the user closes the browser the status in user table can be update because the user didn't click logout.

Do you have any suggestion about this?

Was it helpful?

Solution

(Please note, that whilst the technique here is still somewhat valid; the PHP samples should not be copied verbatim as there are safer means of incorporating user-supplied values in a SQL query)


Instead of storing whether the user is active\inactive, it is better to store some attribute which can be checked against the user on a per-action basis; as in, every time the user tries to do something which requires authentication, it will check to see that this attribute matches before it proceeds.

I recommend you do the following;

First, create a hash to uniquely identify the user whenever they log in. I'd imagine that a sha1 of time() would be enough to avoid collisions. Whatever you choose, make sure that it is varied enough so that another user logging in will have a incredibly low chance of receiving the same hash (for example, don't hash the IP address or browser's user-agent, as these are not varied enough).

Second, store this hash in your database and in the user's session at the time of log in. Doing so will effectively 'log out' the previous user, as the hash should be different each time someone logs in.

Since we're using sessions, a cookie should be automatically placed in the user's browser which will contain a unique ID that identifies the user to his or her session data. The contents of the cookie are not really of concern.

Next, create a function called authenticateUser() or similar, which will be called at the start of every script to ensure the user is authenticated. This script should query the database, checking to see whether a user with your user's ID has a hash that matches your user's hash.

For example:

function authenticateUser($id, $hash, $databaseLink) {
    # SQL
    $sql = 'SELECT EXISTS(
               SELECT 1
               FROM `tbl_users`
               WHERE `id` = \''.mysql_real_escape_string($id).'\'
               AND `hash` = \''.mysql_real_escape_string($hash).'\'
               LIMIT 1
           );';

    # Run Query
    if ($query = mysql_query($sql, $databaseLink)) {
        # Get the first row of the results
        # Assuming 'id' is your primary key, there
        # should only ever be one row anyway.       
        $result = mysql_fetch_row($query);

        # Casting to boolean isn't strictly necessary here
        # its included to indicate the mysql result should
        # only ever been 1 or 0.
        return (bool)($result[0]);
    } else {
        # Query error :(
        return false;
    }
}

Then, we simply pass authenticateUser() the user's ID, hash (per your session data) and a database link (for a database connection you will have to have opened earlier).

If authenticateUser() returns true, the user is authenticated. If false, the user is not OR the database is unavailable or there is an SQL error.

Please note however that this will increase your server load as a database request is sent once per page request. It is probably not all that wise to do this on giant projects where thousands of people are logging in at any given time. I'm sure someone can suggest improvements.

Also, waiting for the cookie to expire is not the best way to force people who have been inactive to log out, as you should never trust cookies. Instead, you can add in an column called last_active which you can update every time the user is authenticated. This will also increase server load, but will allow you to manually override stale log-ins by removing the hash for users who were, say, inactive for 3 hours.

OTHER TIPS

Here's a solution that doesn't require constant database access to work...

(which will avoid the requirement to check the session_id() against the database value every time you request/refresh a page, relieving db/server stress)...

1. On login, grab the pre-existing session_id stored in the DB for this user and do this:

session_id("the pre-existing session id in the database goes here");
session_start();
session_destroy();

2. Then start a new session and save this new session_id to the database, overwriting the previous one. This will logout the previous session on this user if there is one active (effectively logging out the other guy using this account).

Give it a try and let me know if that does the trick!!

You could change your model so that only the most recent user can be logged in.

If you record the most recent session id seen for each user, when they log in a second time you can trash any currently existing session, effectively logging them out.

For a normal user, things appear to "just work". If you're wanting to prevent "abnormal" users from distributing their login credentials, this should serve as a disincentive.

What you should do is check on whether they have been active the last several minutes when trying to login. This could be done with a lastonline stamp and should be set on every page request in the user table.

If not done with javascript you could check, when logging on, if the user was active the last 15 minutes. If not you can login as the new user.

You could also do it with javascript. Make an ajax call that fires every minute or so.

<script>
setInterval(function() {
  // do the ajax call
}, 60000);
</script>

Let this call go to a script that will edit the lastonline stamp in the user db. When trying to login you check the user db if the lastonline stamp has exceeded the minute and you have your check if you may login. This will help when you are on the page but you are not active the last 15 minutes and you do not want somebody else to login.

You need to create a unique ID and store that in a database. What I did was create too. I store one in a session variable and use that to prevent session hijacking and another in a database to prevent multiple logins. The following code will create a unique ID:

$unique_id = sha1('xzr4'.gethostbyaddr($_SERVER['REMOTE_ADDR']).$random_string.$_SERVER['HTTP_USER_AGENT'].'f8k2');

If the unique ID doesn't match, you simply log the user out.

Using client side javascript in order to track logged in user is unreliable.

You can get the same result by simply creating a lastlogindate field in the db, and updating it with the last login timestamp of the user.

At every login attempt, if now()-$lastlogindate > predefined_timeout, then you should accept the new login, otherwise refuse it.

This solution is similar to prograhammer's, but doesn't require you to mess around with switching sessions. It doesn't require you to access the database on every page and doesn't lock out the user after they failed to log out.

Add a field for sessionID to your user table in the database.

Set the default session handler before calling session_start() (needed for the next line of code to work):

session_set_save_handler(new \SessionHandler());

On every successful login, retrieve the stored $sessionID from the database. Destroy the old session with:

(new \SessionHandler())->destroy($sessionID);

Get the new session ID with:

$sessionID = session_id();

Store the new session ID to the database.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top