检测 session_id(some-id) 是不是存在[重复]

Posted

技术标签:

【中文标题】检测 session_id(some-id) 是不是存在[重复]【英文标题】:Detect whether session_id(some-id) exists [duplicate]检测 session_id(some-id) 是否存在[重复] 【发布时间】:2011-12-31 06:54:15 【问题描述】:

可能重复:Detect if php session exists

PHP 手册似乎没有提供检查给定session_id 是否存在的方法。例如,session_id() 有一个可选的id 参数,但它替换了现有的id,而不是按照所需的方法进行查找:session_id_exists(some-id)

为什么我需要检查给定的session_id 是否存在?用例是一项体育订阅服务,其中密码共享已成为问题。登录时,我将用户的会话 ID 存储在 DB 中,并使用它与附加到给定 userID 的任何其他现有会话 ID 进行比较。

为了实现,我需要检查当前会话中是否存在收集的会话ID(密码共享的证明是多个用户同时登录)。

我认为有一种简单的方法可以实现这一点......

【问题讨论】:

没有内置方法可以做到这一点(AFAIK)-我认为您必须循环会话数据目录中的文件并检查相关文件名。我的直接反应是“你可以只用数据库来做到这一点”但现在我想起来了,我不知道如何...... 我看不出获取会话 ID 在这里有什么帮助。会话 ID 在第一次访问会话时生成。两个用户将“永远不会”获得相同的会话 ID。您可以做的是在每个页面视图中检查当前用户的会话 ID 是否与数据库中存储的会话 ID 匹配,如果不匹配,则将用户注销。 对我来说这可能是一个安全功能;即不允许查找与当前用户会话无关的会话数据。我的意图是,在发现多个用户登录单个帐户时,将他们全部注销(取消设置他们的会话),锁定帐户并要求更改密码 @CodeCaster,我宁愿避免每次页面查看方法和数据库查找的开销(尽管很小)。回复:2 个用户永远不会获得相同的会话,这正是重点。如果数据库会话跟踪器包含附加到单个用户 ID 的 1 个或多个记录并且存储的 sessionID 处于活动状态,那么我们知道密码已经泄露——因为尝试登录的用户和已经登录的用户不能是同一个人(还跟踪设备和 IP) @CodeCaster 我认为你有点忽略了这一点 - 他需要做的是检查两个当前存在的会话是否具有与它们关联的相同用户。因此,当用户使用会话 ID 登录时,它会被存储,但如果另一个用户使用相同的用户名但不同的会话 ID 登录,则密码已与某人共享。关键是检查两个会话 ID 是否同时存在 - 可以想象一个合法用户会有两个会话 ID,但一个会过期。这个案例是有道理的,尽管这种方法有点缺陷(例如,如果一个用户使用 2 个浏览器) 【参考方案1】:

你在 index.php 的顶部做这样的事情怎么样

// Update current userid/session record with current timestamp
mysql_query("UPDATE sessions SET last_activity = CURRENT_TIMESTAMP() WHERE user = '$username' AND sid = '".session_id()."'");
// Search for multiple records with timestamp in the last 20 minutes where user id is the same
$result = mysql_fetch_assoc(mysql_query("SELECT COUNT(*) AS current_sessions FROM sessions WHERE user = '$username' AND last_activity > '".date('Y-m-d H:i:s', time() - 1200)."'"));
if ($result['current_sessions'] > 1) 
  // handle duplicates here

您可能不喜欢这种方法,因为它在每次页面加载时都涉及两个数据库查询,但它应该可以工作并且可能比检查会话文件更有效。

【讨论】:

当然这会很好,只是考虑到 imo 不必要的数据库开销,这不是首选方法。如果 cron 作业可以实现锁定共享密码帐户的目标并产生零开销,那么这就是我将首先探索的路线。 @virtualeyes 不想执行单个查询对我来说听起来像是过早的优化。 @CodeCaster,取决于,处理许多并发用户的网站怎么样?它归结为为每个页面请求运行 2 个额外查询(@Dave 的方法)以确定谁在帐户 X 下登录),或者在登录时检查帐户 X 下的活动会话数。后一种解决方案几乎没有没有开销;我会在一段时间后发布它现在测试......【参考方案2】:

根据 CodeCaster 的评论:

除了检查 PHP 会话 ID,您应该针对每个用户连接/断开连接和会话到期,维护当前连接用户的列表(可能还包括连接时间、IP 等),例如在数据库表中。

因此,您将能够检测到单个帐户的多个连接。

您也可以尝试创建一个小脚本循环所有会话文件(如果您使用默认存储),在其上使用“unserialize()”并检查多个会话文件是否具有相同的用户标识符(前提是您将它们存储到$_SESSION)

编辑:因为这应该针对每个用户连接完成,所以 DB 表方法(就像 CodeCaster 建议的那样)似乎更好。

【讨论】:

再次,虽然开销不大,但在单个帐户下登录的多个用户的每个单个页面请求上进行查询是低效的。我现在的倾向是运行一个早期的 AM root cron 作业来审核用户日志,检查白天的同时登录,并相应地锁定帐户(以及发送警告电子邮件)。零开销,并达到锁定共享密码帐户的目标。 @virtualeyes 即使您打算使用 cronjob 执行此操作,您仍然需要存储用户进行活动的时间/会话 ID,这涉及在每个页面上写入文件或数据库无论如何都加载 - 没有办法解决这个问题,因为 PHP 会删除过期的会话文件,所以早上没有数据要检查。 @virtualeyes 实际上,您可以尝试在所有页面中将 session.gc_probability 设置为 0,然后使用您的 cronjob 手动进行垃圾收集,但我不建议这样做,除非服务器是专用于该站点且仅该站点-即使那样也有风险。 @Dave,不正确,登录时我会存储用户 ID、IP、logTime、浏览器等。否则,对于页面到页面的请求,我什么也不做。审计跟踪在日志表中。对于共享密码帐户,只需查看日志就很清楚(几乎同时登录但不同的 IP 和不同的浏览器) @virtualeyes 但它不是“在 x 时间内登录”,而是“在 x 时间内活跃”。如果我在上午 9 点登录并整天保持登录状态,而其他人在下午 4 点使用相同的详细信息登录,则仅检查登录时间不会发现 2 个用户同时使用相同的用户名登录的事实时间,因为登录时间相隔 7 小时......因此,您需要记录活动,而不是登录【参考方案3】:

您应该创建自己的会话状态实现,为您的用例所需的操作提供接口。

class SessionState

    public function idExists($id)
    
        $gateway = new SessionGateway();
        $result = $gateways->searchById($id);
        # ... 
    

如果你已经实现了细节,你可以在你的应用程序中使用对象。

【讨论】:

这相当稀疏,我假设会话状态实现是指将会话状态存储在数据库中而不是 php 默认的内存实现?不想把我的生命献给这个项目,一个快速而肮脏的解决方案就足够了(@DaveRandom 目前位居榜首),php 令人不快以至于无法重新访问,更不用说再次做实际工作的想法了 ;-) Lolz,尽管它很稀疏,但它的编码速度却是一样快。无论如何,这完全独立于您选择的存储层。网关正在处理这个问题,只需做您需要做的任何事情。如果您稍后针对文件数据库存储更改为基于 RDBMS 的存储,请执行此操作。如果您需要会话文件解析器,请查看 Serialized 库。 问题是,我不知道你在说什么,也没有其他人基于缺乏 cmets/upvotes。但是,鉴于您的 SO 排名很高,很明显您知道自己在说什么 ;-) 因此,我的意思是“相当稀疏”,您是否愿意详细说明您的意思,例如“new SessionGateway()” ?换句话说,SessionState 类还没有触发一个 eureka 时刻,尽管它可能有更多细节和/或链接到示例实现。谢谢... @virtualeyes:好吧,忘掉模式吧,这些模式或多或少地封装了你的情况变化,应该让你的应用程序更容易处理整体问题。 @virtualeyes:但是您更倾向于解决一个问题:您希望用户 ID 是会话 ID。您可以使用秘密对其进行加密,然后将其用作session_id(您已经知道,您可以设置它)。旁边将 IP 存储在会话中。如果用户重新使用具有不同 IP 的会话,则会触发密码重置。您可以通过一个额外的会话(按顺序使用两个会话)轻松地做到这一点,即当前一个会话和新的 userid-one。只是不要为新的 cookie 使用它,这样它就会保持私密。过滤那些无法注入用户会话 ID 的公共会话 ID。【参考方案4】:

首先,这是对防止密码共享方法的一次很好的探索。

我对基于每个请求(即@DaveRandom 提出的 2 查询解决方案)跟踪用户活动的直觉反应是啊,不!正如@CodeCaster 指出的那样,这可能是过早优化的情况,但是嘿,我们有一个小(几千)但***的用户群,随着曲棍球赛季的开始和比赛结果的到来,他们会非常高兴。该网站有多年来一直在快速运行,不想摇摆不定,这是一项付费服务​​,因此性能必须出色。

好的,解决方法:

apache 用户对会话目录中的会话文件具有读/写权限。通过在登录时记录 session_id,我们可以锁定共享密码帐户。登录成功:

- Loop through stored session ids related to target account
- if /path/to/session-id-file not empty, increment login counter
- if login counter exceeds number of users allowed for a given plan:
- delete all session files related to target account
- lock the account and force a password reset

开销最小,实现很简单,问题解决了。

注意:我原本以为在不创建安全循环的情况下访问会话目录文件是不可能的;然而,情况并非如此(至少在我的 CentOS 5 设置中)。您无法获取与当前用户会话无关的 session_id,但您可以存储给定用户的会话 ID 并访问存储其会话的会话文件,从任何用户的会话(包括删除文件)。关键是有会话ID来查找相应的/path/to/session-file

【讨论】:

以上是关于检测 session_id(some-id) 是不是存在[重复]的主要内容,如果未能解决你的问题,请参考以下文章

SQL server 数据连接池使用情况检测

jQuery选择函数

jQuery DOM

JQuery常用函数及功能小结

Jquery常用函数及功能

JQuery常用函数及功能小结