Jump to content

A twister - Unable to prevent a second login by the same member.


ajoo

Recommended Posts

Hi all, 

 

I have another problem associated with logins using sessions and stuff.  

 

On my machine I have Apache and mysql installed. The browser I use is Google Chrome. Everything is working fine.

 

There is a very simple login system which, like all login systems, has a ID and password field. The user has to fill in the ID and password and these are checked against a database and the user is logged in. However before the ID and password are checked, the code checks if the $_session variable is set to and if it is it declares that the user is logged in and redirects the user to a secure page. There the user sees a personalised message and can logout.  This is what is desired but this is not happening. Find below the code. There are 5 files namely 1. index.php,  2. loginproc.php   3. securedpage.php   4. logout.php   and  5. config.inc

 

In the file INDEX.PHP,is the following bit of code 

 

///////////////// CODE NEVER TRAVERSED ///////////////////// RED RED RED //////////////

// Check, if user is already login, then jump to secured page
if (isset($_SESSION['username'])) {
echo $_SESSION['username']. "Already Logged in @ index 8";
echo " You are being logged out as you have logged in from another page ";
session_destroy();
header('Location: index.php');


///////////////// CODE NEVER TRAVERSED ///////////////////// RED RED RED //////////////

 

 

which never gets executed  and I wonder why. These lines check at the outset if the user is logged in. If he is logged in and an attempt is made to login again these lines should be executed terminating the first session or at least that's what I want but for some baffling reason that does not occur. I try and create that scenario as follows. I log into the system once thru google chrome. Then i use another tab to login using the same ID and password. And to my surprise i am logged in and reach the secured page again. So I then have 2 logins on 2 different browser pages by the same ID and in both browsers the secured page is displayed. What I am trying is ofcourse that once a person is logged in and another login attempt is made, the first session be destroyed and a notification to that displayed on the first logged in browser page. 

 

I am unable to see where the mistake lies. I would be most grateful for any help and suggestions. 

 

Thanks all.

<?php

///////// INDEX.PHP /////////////////////

// Inialize session
session_start();
///////////////// CODE NEVER TRAVERSED ///////////////////// RED RED RED //////////////

// Check, if user is already login, then jump to secured page
if (isset($_SESSION['username'])) {
echo  $_SESSION['username']. "Already Logged in @ index 8";
echo " You are being logged out as you have logged in from another page ";
session_destroy();
header('Location: index.php');

///////////////// CODE NEVER TRAVERSED ///////////////////// RED RED RED //////////////


} else { echo " Hi new user ";}

if (isset($_SESSION['username'])) {echo  $_SESSION['username']."Already Logged in @ index 14";}


?>
<html>

<head>
<title>PHPMySimpleLogin 0.3</title>
</head>

<body>

<h3>User Login</h3>

<table border="0">
<form method="POST" action="loginproc.php">
<tr><td>Username</td><td>:</td><td><input type="text" name="username" size="20"></td></tr>
<tr><td>Password</td><td>:</td><td><input type="password" name="password" size="20"></td></tr>
<tr><td> </td><td> </td><td><input type="submit" value="Login"></td></tr>
</form>
</table>

</body>

</html>



<?php

///////// LOGINPROC.PHP ////////////

// Inialize session
session_start();

// Include database connection settings
include('config.inc');

// Retrieve username and password from database according to user's input
$login = mysql_query("SELECT * FROM members WHERE (Username = '" . mysql_real_escape_string($_POST['username']) . "') and (Password = '" . mysql_real_escape_string($_POST['password']) . "')");
echo " Login = $login";
// Check username and password match
if (mysql_num_rows($login) == 1) {
// Set username session variable
echo " Ok Hi there - Welcome ";
$_SESSION['username'] = $_POST['username'];
// Jump to secured page
header('Location: securedpage.php');
}
else {
// Jump to login page
echo " Can't find you";

//header('Location: index.php');
}

?>


<?php

/////////////////// SECURED PAGE ////////////////////

// Inialize session
session_start();

// Check, if username session is NOT set then this page will jump to login page
if (!isset($_SESSION['username'])) {
header('Location: index.php');
} else { echo " Welcome". $_SESSION['username']; }

?>
<html>

<head>
<title>Secured Page</title>
</head>

<body>

<p>This is secured page with session: <b><?php echo $_SESSION['username']; ?></b>
<br>You can put your restricted information here.</p>
<p><a href="logout.php">Logout</a></p>

</body>

</html>

This is content of ‘logout.php’:

<?


<?php

/////////// LOGOUT. PHP ///////////////////
// Inialize session
session_start();

// Delete certain session
unset($_SESSION['username']);
// Delete all session variables
session_destroy();

// Jump to login page
header('Location: index.php');

?>



<?php

////CONFIG.INC /////////////////

$hostname = 'localhost';        // Your MySQL hostname. Usualy named as 'localhost', so you're NOT necessary to change this even this script has already online on the internet.
$dbname   = 'test'; // Your database name.
$username = 'root';             // Your database username.
$password = '';                 // Your database password. If your database has no password, leave it empty.

// Let's connect to host
mysql_connect($hostname, $username, $password) or DIE('Connection to host is failed, perhaps the service is down!');
// Select the database
mysql_select_db($dbname) or DIE('Database name is not available!');

?>


Edited by ajoo
Link to comment
Share on other sites

  • Replies 50
  • Created
  • Last Reply

Top Posters In This Topic

each of your header() redirect statements need an exit statement after it to prevent the rest of the code on the page from running.

 


 

you also cannot echo or output any characters to the browser before a header() statement. if you are actually seeing the echoed output, the header() statement won't work at all and if you are not seeing the output and the header() is working it means that php is buffering output and echoing things to help you debug won't work.

Link to comment
Share on other sites

Thanks for the advise. I'll look into it and revert with the outcome. Is there any way that i can make the program to pause for a few seconds after the echo commands to be able to check the program flow for debugging purposes? Thanks again.

 

Ok I have just made the changes with the exit(); at the appropriate places as you advised and reran the login to be met with the same results. However if you can suggest some way for me to pause the program and check the output of the echo commands, it may give me a great insight into the program and thus maybe an approach to the solution.

 

Eagerly awaiting a reply to this and thanks all.  

Edited by ajoo
Link to comment
Share on other sites

I usually just change the header() to die() until I figure out the problem.

 

At any rate:

What I am trying is ofcourse that once a person is logged in and another login attempt is made, the first session be destroyed and a notification to that displayed on the first logged in browser page.

There is no relationship between the two sessions. They are distinct client sessions (only related to the page request), one session cannot know about the other. The solution would require

  • on login, write the session ID to the user's database record.
  • on page access, if the session ID of the request does not match the one in the database (for the user in the $_SESSION array), tell them they logged in from somewhere else, and kick them to the logout script.
That's off the top of my head, I've never tried this.
Link to comment
Share on other sites

Just run it on a token system.  If they don't have the correct token, they get logged out, or never logged in.  This would be set inside the session, and not the database.  A simple md5 of the browser (HTTP_USER_AGENT) should suffice for what you are looking to do.

 

Tokens can be set in a cookie, or in the URI.

Link to comment
Share on other sites

Hi. Thanks all for their replies. I am surprised to find that my last reply to this post here is missing and so I'll repost that with a new question. Firstly I tried all the advise by Mac and it all worked great. I used that and now have a login system that prevents multiple login by the same person twice. So thanks for that. I guess to have it working in a real system I would need to use a database as suggested by David. Once i do that I'll post my results here again. I am not aware of the token system suggested by jcbones but i'll explore that too. If you could provide me wid some more information on tokens or a reference site I'll be happy. 

 

So now my system works with a small hitch. Like I mentioned that I would like the user to get logged out of the first session once he logs in the second time from the same or new machine. With my code and help from you all what i have achieved is the prevention of the second login. I'ld be grateful if someone can suggest how I might be able to ensure that the 1st session of the user is terminted when he logs into the sytem a second tme. This should be somewhat like a yahoo chat login system which logs out the user with a message which says that the user has been logged out because he has logged in from another machine, once the second login by the same user occours. 

 

Thanks loads.
Link to comment
Share on other sites

Why not do something like this?

if ($_SESSION['destroyed_on_dupe']){
echo "This session was destroyed because you logged in from....";
// maybe destroy the session here
die();
}
elseif (isset($_SESSION['username'])) {
echo  $_SESSION['username']. "Already Logged in @ index 8";
echo " You are being logged out as you have logged in from another page ";
$_SESSION['destroyed_on_dupe']=true ;
unset($_SESSION['username'])
die();
}
Link to comment
Share on other sites

Hi david, Just by looking at the code, because i tried something like this, I feel that this would logout from the second session and not the first session. So it would probably not allow a second login. Where as what I am trying to achieve is that the 2nd login occurs destroying the first.

 

Isn't there any function that allows the sessions to be destroyed by name. for eg. If i have jack logged in at time = t1 and then jack logs in again at a later date t2, is there no function which can identify and then logout jack at t1 with a message appearing on the screen of Jack at t1. ( session 1) 

 

In case i am missing something that you suggested then kindly rephrase it for me.

 

Thanks all.  

Link to comment
Share on other sites

you could do an active system, when a user logs in, get the script to change the users status in the database to "Active" when they log in. This way, when they try to log in from another window, browser, computer, etc... all you have to do is check if a cell in that users row is "active" if it is, then the user is already logged, if it isn't active, then obviously allow the user to log in.

Link to comment
Share on other sites

You are right. Here's how I would approach it.

 

Create a table session_status. Fields: username, session_id (autoincrement), timestamp, status

If a user logs in check the table for other entries with same user ID - change status to "logged out because new login"

Create a new record with status = "active"

set $_SESSION['session_id']

 

If a user accesses a page and is already logged in check the status of the record that matches his sessioin_id to make sure that he hasn't been logged out (status must = acitve).

Edited by davidannis
Link to comment
Share on other sites

The problem still remains that which set of commands would target the first session on say computer1 to terminate it while the same user is trying to logon from computer 2 without having logged off from the first machine. The program is idle an computer1 and the code is being executed on machine 2. How then would that code on machine 2 trigger code on machine 1 to log out from there ?  Can you please try and demonstrate with some actual code? At least a pointer. Thanks.

Link to comment
Share on other sites

This works for me:

Login page:

<?php
session_start();
$link = mysqli_connect("localhost", 'XXXXX', 'XXXXXXXX', 'testdb') or die("Unable to connect!");
if ($_POST['username']!=''){// this is someone logging in
    //in the real world we'd check for a valid password here
    $query="UPDATE sessions SET status='X' WHERE username='".$_POST['username']."'";//make all other sessions for this user die
    $result=mysqli_query($link, $query);
    $username=$_POST['username'];
    // add a record to the database
    $query="INSERT INTO sessions VALUES ('', '$username','A')";
    $result=mysqli_query($link, $query);
    $_SESSION['id']=mysqli_insert_id($link);
    $_SESSION['username']=$_POST['username'];// Now this session is logged in
    echo 'login success';
    die();
}
?>
<!--
To change this template, choose Tools | Templates
and open the template in the editor.
-->
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title></title>
    </head>
    <body>
        <form method="POST" action="testlogin.php">
            <input type="text" name="username" value="david">
            <input type="submit">
        </form>
        <?php
        
        ?>
    </body>
</html>

Regular Page:

<?php
session_start();
$link = mysqli_connect("localhost", 'XXXXXXX', 'XXXXXXXX', 'testdb') or die("Unable to connect!");
if ($_SESSION['username']!=''){
    $query="SELECT status FROM sessions WHERE id='".$_SESSION['id']."'";
    $result=  mysqli_query($link, $query);
    $sessions=  mysqli_fetch_assoc($result);
    if ($sessions['status']=='X'){
        echo 'you were booted';
        die('too bad');
    }
    
}else{
    die ('you need to login');
}

?>
<h1>YOu are logged in</h1>

You'll need to hash, salt, and check passwords, sanitize input, and catch errors. This is quick and dirty to demo the idea.

Link to comment
Share on other sites

each of your header() redirect statements need an exit statement after it to prevent the rest of the code on the page from running.

No it doesn't. When you do a header redirect, anything after it basically doesn't exist. even if you put an exit() it would never be reached.

 

Be sure if you're going to use an active flage to identify logged in users in the database, that you also check at random intervals to toggle the status of users that did not log out but are not actively on the page.  The easiest way to do that is probably with a time of the last time they loaded a page.  it would take just a second to add that to the database, and then on login, instead of checking just the active flag, you can see when they were last active, someone with an active flag for example that hasn't loaded any page but login in the last hour is probable safe to start a new session for. You can also use the actual session id's in the database, so that you delete the session file and essentially kill all $_SESSION variables for the previously active session. 

Edited by seandisanti
Link to comment
Share on other sites

someone with an active flag for example that hasn't loaded any page but login in the last hour is probable safe to start a new session for.

I think that the OP wanted to start a new session any time someone logged in and simultaneously kill the old one (even if it was very recent.

 

I do agree that there ought to be a time stamp in the sessions table and you should just go through and clear the old ones (Active or not) on a regular basis so that you don't end up with an enormous table.

Link to comment
Share on other sites

 

each of your header() redirect statements need an exit statement after it to prevent the rest of the code on the page from running.

No it doesn't. When you do a header redirect, anything after it basically doesn't exist. even if you put an exit() it would never be reached.

 

Oh yeah?

 

header('Location: http://www.google.com');
file_put_contents('test.txt', 'Supposedly never reached...');
sleep(1000); // in this time you may want to read php.net while you wait to be redirected 
Run this code, then check your folder.

 

You might wanna read up on what header() does exactly: http://php.net/manual/en/function.header.php.

 

If output_buffering = Off then you will not have to wait 1000 seconds. Maybe even the file won't be written.. This is the reason why you should always add an exit after every header location call.

Edited by ignace
Link to comment
Share on other sites

I stand corrected, the die command is never reached in this code, and I thought that the behavior was the same regardless of the functions following the header.

 

<?php
header('Location:http://google.com');
die('die reached');

Thanks for the info!

Link to comment
Share on other sites

Hi all, 
 
OK here i am with the results of my experiments with the code for a login system which boots out any previous and running sessions by a given ( particular) user, thus effectively disallowing a multilogin by a single user. So anytime a user forgets to logout and logs back in again at the same or new machine, the previous sessions are to be closed and destroyed and the user gets to login from whereever he's logging in once again. At least thats what I am trying to achieve and I appreciate the help of so many senior members here. 
 
Attached please find a snapshot of my computer screen which show the above not happening. The code suggested by david works in the database all right as can be seen from the snapshot. Also you can see that I have been able to login on 3 pages on the same machine. 
 
David gave two files one for login which i renamed as status.php and one regular.php which I assumed was the page to be displayed on successful login. The regular.php is supposed to evict an already logged user if he logs in a second time from any other machine. However one look at the regular.php shows that it simply checks the status field and if its been changed to 'X' indicating a previously active session, it is supposed to simply echo that the user has been booted.
 
This part of the code however never gets to execute and so this eviction message " you were booted" is never displayed. I think thats because the regular pages have already run their length in the previous sessions. Its here that the new active session some how has to communicate with previous sessions and trigger / refresh the page code once again and then actually destroy the old session evicting the user in the previous page and displaying the message. 
 
How is this triggering / refreshing of the browser in the previous session to be achieved. I guess the problem has still not really been solved. Or maybe I am missing something here or are too much of a newbie to get the drift of the experts. A little more help with actual code would feel great! 
 
Thanks all. Looking forward to a finally a complete solution to this one. I hope this would help other new comers like me.

 

( I am unable to paste the snapshot of my run of the program. If someone can tell me  how i may achieve that I'll paste it the next time round ).

 

 

 

Link to comment
Share on other sites

If the problem is that the regular page you need to make sure that the page is not cached. http://stackoverflow.com/questions/49547/making-sure-a-web-page-is-not-cached-across-all-browsers To see if that is really the problem, hit the refresh button and see if you get the message.

 

Another way to do it, which would be more elegant, would be to write a routine that would periodically check the sessions database using AJAX and force the refresh only if the status had changed to X

 

You can also put an http refresh meta tag in the regular page or force a refresh using javascript.

 

I'm not sure why it is important to log out an already displayed page. As soon as the user goes to the next page on the site, he'll get the "you have been booted" message. Presumably he's already seen the content of the page he's on.

Link to comment
Share on other sites

Hi david. Thanks for the revert. Though I have not tried it, but i am quite certain that a manual refresh would cause the page of the previous sessions to display the "you have been booted" message.  Though I don't understand the cache bit that you mentioned. 

 

In fact the only thing that's needed in this problem is as you have suggested that there should be some routine which would periodically check the database, something like an interrupts in java or c or Ajax as you have mentioned here. Can you please elaborate on the Ajax bit. I am very new to PHP and have zero knowledge of Ajax as i have not needed it till now. So if you give me some pointers to it maybe then i'll try and implement the full logic. Maybe a tutorial on Ajax that implements these "periodic checks". And if its not too complex, a small bit of code to do just that.

 

Again about refresh metatags or javascript  I have no idea. please shed some light on their usage as well. 

 

I feel its important to log out of the previous sessions when the user attempts a fresh login. A user can forget to logout of a session and then maybe logs in at another place. He may not go back to the old machine that day and so there would be no one to refresh that page to get a final logout and so a clean up of the database where logins status is stored will not occour. I want to do a clean up of the database when i logout a user from a previous session so that this login database may not become too large. 

 

Would it be a good idea to delete the records that have been marked "X" and are therefore in the "booted" status? Since this particular logic provide the "id" as the row number, deleting rows can cause a mix up of values thereby rendering the "id" not unique. Just a thought.

 

Looking forward to some more discussion and help. 

Thanks loads.

Link to comment
Share on other sites

He may not go back to the old machine that day and so there would be no one to refresh that page to get a final logout and so a clean up of the database where logins status is stored will not occour.

You can timestamp the rows in the sessions database as previously suggested and have a script go through and delete all of those more than a day or two old. If you try to write the code to do that first, I will help you with any problems.

 

Would it be a good idea to delete the records that have been marked "X" and are therefore in the "booted" status? Since this particular logic provide the "id" as the row number, deleting rows can cause a mix up of values thereby rendering the "id" not unique. Just a thought.

 

I would not delete the records right away because then you just have a missing session record and don't know that they have been logged out when they try again from the firs computer. If you make the id column auto-increment it will not fill in ids that have been deleted. MySQL will just assign the lowest number that has never been used (though you can force it to reuse an number).

Link to comment
Share on other sites

Though not very efficient you can easily get a message to the first computer by adding this between the head and /head tags:

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="refresh" content="30">

It may cause other problems if you have a user filling out a long form, etc. Not sure what you are trying to accomplish by booting users before they leave the current page if they log in elsewhere. As discussed above, this is not a solution to keeping the sessions table clean, but a solution to not seeing a message right away in the first browser when somebody logs in a second place.

Link to comment
Share on other sites

Hi, Thanks david for the response. I am going to try first the meta tags in the header and will revert. Wish you had given me something on Ajax for the refreshing of the page and I would have checked that one out as well but I'll search it on google and revert. Once this is done I'd like to discuss some more regarding the cleaning up of the database.

Thanks. 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.