如何防止PHP网站中的多次登录

时间:2009-11-13 08:37:11

标签: php security

我想阻止php应用程序中的多次登录。

首先,我在用户表中创建登录状态(活动,非活动)。

当用户A登录时,用户状态将设置为“活动”,如果用户注销,状态将设置为“非活动”。当另一个客户端尝试使用相同的用户帐户登录时,我检查用户表。如果用户仍处于活动状态,则会将错误登录信息发送给用户。

发生问题,如果用户关闭浏览器,则可以更新用户表中的状态,因为用户没有单击注销。

你对此有什么建议吗?

8 个答案:

答案 0 :(得分:19)

(请注意,虽然这里的技术仍然有点有效;但不应逐字复制PHP示例,因为在SQL查询中有更安全的方法来合并用户提供的值)


不是存储用户是否处于活动状态\非活动状态,而是最好存储一些可以基于每个操作对用户进行检查的属性;就像在每次用户尝试执行需要身份验证的操作时一样,它会检查此属性是否匹配,然后再继续。

我建议您执行以下操作;

首先,创建一个哈希值,以便在用户登录时唯一标识用户。我认为sha1 time()足以避免冲突。无论您选择什么,请确保它的变化足以使另一个登录用户接收相同哈希的机会极低(例如,不要散列IP地址或浏览器的用户代理,因为这些不是变化的足够)。

其次,在登录时将此哈希存储在您的数据库和用户的session中。这样做会有效地“注销”前一个用户,因为每次有人登录时哈希都应该不同

由于我们正在使用会话,因此应该在用户的浏览器中自动放置一个cookie,该浏览器将包含一个唯一的ID,用于标识用户的会话数据。 cookie的内容并不值得关注。

接下来,创建一个名为authenticateUser()或类似的函数,该函数将在每个脚本的开头调用,以确保用户通过身份验证。此脚本应查询数据库,检查具有用户ID的用户是否具有与用户散列匹配的散列。

例如:

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;
    }
}

然后,我们只是传递authenticateUser()用户的IDhash(根据您的会话数据)和database link(对于您必须打开的数据库连接)更早)。

如果authenticateUser()返回true,则会对用户进行身份验证。如果false,则用户不是OR数据库不可用或存在SQL错误。

但请注意,这会增加您的服务器负载,因为每个页面请求会发送一次数据库请求。在数千人在任何特定时间登录的巨型项目中执行此操作可能不是那么明智。我相信有人可以提出改进建议。

此外,等待cookie过期并不是强制非活动人员注销的最佳方式,因为您永远不应该信任cookie。相反,您可以添加一个名为last_active的列,您可以在每次验证用户时更新该列。这也会增加服务器负载,但允许您通过删除hash用户,例如,非活动3小时来手动覆盖过时登录。

答案 1 :(得分:7)

这是一个需要不断数据库访问才能工作的解决方案......

(这样可以避免每次请求/刷新页面时都要根据数据库值检查session_id(),从而减轻数据库/服务器压力)...

1。登录时,抓取存储在此用户数据库中的预先存在的session_id并执行以下操作:

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

2. 然后启动一个新会话并将此新session_id保存到数据库,覆盖前一个会话。如果有一个活动的话,这将注销该用户的上一个会话(使用此帐户有效地注销其他人)。

尝试一下,让我知道是否有诀窍!

答案 2 :(得分:6)

您应该做的是检查他们在尝试登录时的最后几分钟是否处于活动状态。 这可以使用lastonline标记来完成,并且应该在用户表中的每个页面请求上设置。

如果没有使用javascript,您可以在登录时检查用户是否在过去15分钟内处于活动状态。如果没有,您可以以新用户身份登录。

你也可以用javascript来做。进行每分钟左右发射的ajax调用。

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

让此调用转到将编辑用户数据库中的lastonline标记的脚本。 当您尝试登录时,检查用户数据库是否上一个在线邮票超过了分钟,并检查您是否可以登录。当您在页面上但过去15分钟内您没有活动而且您不希望其他人登录时,这将有所帮助。

答案 3 :(得分:5)

您可以更改模型,以便只能登录最近的用户。

如果您记录每个用户看到的最新会话ID,当他们第二次登录时,您可以删除任何当前存在的会话,从而有效地将其记录下来。

对于普通用户来说,事情似乎“正常”。如果您想阻止“异常”用户分发他们的登录凭据,这应该起到抑制作用。

答案 4 :(得分:1)

您需要创建一个唯一的ID并将其存储在数据库中。我所做的也是创造。我在会话变量中存储一个并使用它来防止会话劫持,而在数据库中使用另一个来防止多次登录。以下代码将创建一个唯一ID:

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

如果唯一ID不匹配,您只需将用户注销即可。

答案 5 :(得分:0)

使用客户端javascript来跟踪登录用户是不可靠的。

只需在数据库中创建lastlogindate字段,并使用用户的上次登录时间戳更新它,即可获得相同的结果。

每次登录尝试时,如果现在() - $ lastlogindate&gt; predefined_timeout,那么你应该接受新的登录,否则拒绝它。

答案 6 :(得分:0)

此解决方案类似于前卫的解决方案,但不需要您搞乱切换会话。不需要您访问每个页面上的数据库,并且在用户注销失败后也不会锁定用户。

将sessionID的字段添加到数据库中的用户表中。

在调用session_start()之前设置默认的会话处理程序(需要下一行代码起作用):

session_set_save_handler(new \SessionHandler());

每次成功登录后,从数据库中检索存储的$ sessionID。使用以下命令破坏旧会话:

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

使用以下方法获取新的会话ID:

$sessionID = session_id();

将新的会话ID存储到数据库中。

答案 7 :(得分:-1)

简单你可以检测浏览器关闭onbeforeunload()调用php更新