5

我使用 redis 作为会话存储,

像这样存储会话

[NameSpace]:[UniqueId] -> [email_id]

问题来了,

当用户重置密码时,如何使该用户的所有会话无效?

这是我想出的解决方案,

将电子邮件 ID 存储为 UID 的一部分

像这样存储会话

[NameSpace]:[UniqueId]-[email_id] -> [email_id]

然后我可以SCAN MATCH在用户重置密码时删除所有密钥。

维护 UID 列表

在存储会话之后

[NameSpace]:[UniqueId] -> [email_id]

维护一个单独的列表

[NameSpace2]:[email_id] -> [ "[UniqueId]", "[UniqueId]" ]

并使用列表使会话无效。(我可以使用redis命名空间 pubsub 来保持上述列表的有效性)

我的问题是

  1. 在 redis 中进行批量会话失效的推荐方法是什么?
  2. [UniqueId]-[email_id]像在 cookie 中一样存储会话 ID 是否存在任何安全问题?

PS:我知道有一个类似的问题,但我觉得它很吵,而且面向 express.js,而不是通用的 redis 和用户会话。(使 express.js 中单个用户的所有会话无效

4

3 回答 3

2

维护一组所有用户会话

[NameSpace]:[email_id] -> {}

每个会话标识符都是集合中的一个值。如果您想存储会话属性,请使用地图sessionIdentifier1 -> sessionProperties1(查找和删除成本相同)

[NameSpace]:[email_id] -> {sessionIdentifier1 , sessionIdentifier2}

批量会话失效-删除密钥[NameSpace]:[email_id]。成本 O(1)。

查找key的 sessionIdentifier - SISMEMBER 。成本 O(1)。sessionIdentifier1[NameSpace]:[email_id]

在 cookie 中存储像 [UniqueId]-[email_id] 这样的会话 ID 是否存在任何安全问题?

这取决于。如果 cookie 不是HttpOnly,它将允许恶意 JS 通过 XSS 漏洞读取该 cookie。您可能会让自己受到网络钓鱼攻击。您可以改用用户的内部UUID

于 2015-02-06T22:09:27.293 回答
1

Based on your examples, it appears you are storing sessions as a key-->single value pair, and that a user can have multiple sessions. There are a few ways you can deal with this.

  1. Use a very short expiration and refresh on page load. As part of the password changing code you pull the session ID from the user's session and delete it. The short expiration ensures old sessions are removed.

  2. Use a hash or set of hashes. Depending on user count you can either use a single hash, if "small" user base, or hash them into buckets. You can then store namespace:sessions[userid] -> email for your session. At this point you can easily remove the single user session,and you don't have duplicate sessions. You do lose the ability to automatically expire sessions.

  3. Use hashes, sorted sets, and a scheduled task for cleanups. In this method you use the session ID as the key to a hash which has session keys and their values. Two of those members are creation and last seen time stamps. You can then use another hash for each user which maps sessionid to last seen time stamp. With this you keep the update on page refresh from option 1, but you have an easy to get list of sessions for each user. This does not, however, prune old sessions. You can expire a hash, but you'd have to either expire both and update both on page view, or us a scheduled task.

For that you can use a sorted set which has session ID as the member and timestamp as the score. Now you can use standard sorted set queries such as zrangebyscore to get all sessions older than a certain timestamp into a list, then delete those keys and members.

You could prevent multiple session with this option by having your session creation code check the user's session hash for existence and either use expiration or have the session creation code check for last update being within your active session criteria and re-use it or clear transient members from it. Because you either have a single session or can easily pull a list, and hopefully have expiration and/or a scheduled task to prune old ones, managing sessions becomes much easier - and without the need for scan or keys.

I'd recommend single-session per user and liberal use of expiration.

于 2015-02-07T15:27:35.567 回答
1

回覆。1 - 由于活动用户会话的数量(大概)远低于数据库中的会话总数,我会去手动管理每个用户的活动会话而不是SCANning。但是,我会为每个用户使用一组来存储活动会话密钥标识符,而不是一个列表,因为您希望能够有效地从中添加和删除活动会话。如果用户有很多活动会话需要批量失效,您可以SSCAN使用SMEMBERS.

回覆。2 - 如果有人进入您的应用程序/数据库服务器,会有很多安全问题 :)

于 2015-02-06T15:23:40.483 回答