php PHP网站如何防止多次登录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1727919/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 03:39:19  来源:igfitidea点击:

How to prevent multiple logins in PHP website

phpsecurity

提问by Adi Sembiring

I want to prevent multiple logins in a php application.

我想防止在 php 应用程序中多次登录。

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.

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

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?

你对此有什么建议吗?

采纳答案by Lachlan McD.

(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)

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



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 sha1of 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).

首先,在用户登录时创建一个哈希来唯一标识用户。我想 a sha1oftime()就足以避免冲突。无论您选择什么,请确保它有足够的变化,以便其他登录的用户收到相同哈希值的机会极低(例如,不要对 IP 地址或浏览器的用户代理进行哈希处理,因为这些没有变化)足够的)。

Second, store this hash in your database and in the user's sessionat 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.

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

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.

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

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

然后,我们简单地传递authenticateUser()用户的ID, hash(根据您的会话数据)和 a database link(对于您必须更早打开的数据库连接)。

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.

如果authenticateUser()返回true,则用户已通过身份验证。如果false,则用户不是或数据库不可用或存在 SQL 错误。

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_activewhich 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 hashfor users who were, say, inactive for 3 hours.

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

回答by prograhammer

Here's a solution that doesn'trequire constant database accessto 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)...

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

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

1.登录时,为该用户获取存储在数据库中的预先存在的 session_id 并执行以下操作:

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

2.然后开始一个新的会话,并将这个新的 session_id 保存到数据库中,覆盖之前的。如果有一个活跃用户,这将注销该用户的前一个会话(有效地注销另一个使用此帐户的人)。

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

试一试,让我知道这是否有用!!

回答by Robert Cabri

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.

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

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.

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

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

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

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

让这个调用转到一个脚本,该脚本将编辑用户数据库中的 lastonline 标记。尝试登录时,如果 lastonline 标记已超过分钟,请检查用户 db,并检查是否可以登录。当您在页面上但最近 15 分钟没有活动并且您不希望其他人登录时,这将有所帮助。

回答by Paul Dixon

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.

如果您记录每个用户最近看到的会话 ID,当他们第二次登录时,您可以删除任何当前存在的会话,有效地将它们注销。

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.

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

回答by Dan Bray

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 hiHymaning and another in a database to prevent multiple logins. The following code will create a unique ID:

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

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

回答by Kai Pommerenke

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.

此解决方案类似于 prograhammer 的解决方案,但不需要您处理切换会话。它不需要您在每个页面上访问数据库,也不会在用户注销失败后将其锁定。

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

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

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

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

session_set_save_handler(new \SessionHandler());

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

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

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

Get the new session ID with:

使用以下命令获取新会话 ID:

$sessionID = session_id();

Store the new session ID to the database.

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

回答by yann.kmm

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

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

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.

您可以通过简单地在数据库中创建一个 lastlogindate 字段并使用用户的最后登录时间戳更新它来获得相同的结果。

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

在每次登录尝试时,如果 now()-$lastlogindate >prefixed_timeout,那么你应该接受新的登录,否则拒绝它。