使用redis使用JWT维护内存中的用户会话
#redis #身份验证 #jwt #session

用例

想象您正在构建下一个亚马逊,该亚马逊允许用户通过移动 / Web应用程序订购任何内容。
用户将登录到该应用程序,他们可以浏览产品,将项目添加到购物车中,然后结帐以使他们喜欢的东西交付到家门口。

允许用户浏览产品或将项目添加到购物车中,该应用将使用HTTP协议向后端进行几个REST API调用。由于HTTP是无状态的,因此我们可以要求用户一次又一次地登录,我们需要一种机制来提供用户上下文,每个呼叫都可以使用后端来确定哪个用户正在与应用程序进行交互。

这是用户会话的来源。用户会话包含有关用户的信息,这些信息允许识别有关用户的经常使用的信息,对于给定的时间范围,直到其有效为止。

它可以包含用户的电子邮件,角色,偏好等等数据

请注意,在会话中维护数据可能很昂贵,不安全且难以扩展,因此我们需要非常谨慎地放入其中的内容

会议如何工作?

我们需要将用户会话存储在可以在每个请求中使用的地方。

我们需要能够信任此信息。例如:如果我们在会话中存储角色,我们需要确保在客户端尚未篡改角色信息,因为客户的信息可以用于身份验证和授权。

因此,我们可以将会话存储在客户端或服务器端。

在客户端存储会话。

如果我们想将会话数据存储在客户端,则只要用户登录我们创建一个用户会话并将其发送回客户端。有多种方法可以做到这一点,但我们将出于本文的目的谈论JSON Web令牌(JWT)。

什么是JWT?

JSON Web令牌本质上是一个长字符串,可以用作将两个系统作为JSON对象之间交换信息的一种方式。由于代币是使用算法(如HMAC)签名的,因此我们可以使用秘密键验证签名,如果它是有效的,我们可以信任令牌中包含的信息。

JWT由3个部分组成

  1. 标题
  2. 有效载荷
  3. 签名

JWT表示为

<header>.<payload>.<signature> 

这是一个示例JWT令牌。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

可以在https://jwt.io或类似的调试器上查看任何其他JWT的内容。

有关JWTS的更多信息,请参见https://jwt.io/introduction

这是我们的用例与JWT的外观:

Image description

  1. 用户使用其凭据登录应用程序。
  2. 服务器通过查询数据库来验证用户。
  3. 服务器使用所需的用户信息和服务器上可用的签名秘密生成JWT令牌。
  4. 然后,服务器将响应标头/ cookie中的JWT令牌发送回客户端。
  5. 当客户发送另一个API调用时,此令牌将在授权标题/ cookie中传递。
  6. 服务器从标题/cookie提取JWT令牌,并使用存储的秘密验证令牌。
  7. 如果验证成功,服务器将使用请求的资源响应,否则,服务器会以401错误响应。

优点

  1. 消除数据库调用:由于所需的数据存储在令牌本身中,因此它完全消除了数据库调用以获取该数据。
  2. 易于扩展:由于状态/数据是在请求本身中传递的,因此它独立于服务器数,并且可以轻松缩放该解决方案。

缺点

  1. 成本:客户和服务器之间交换的数据数量将随着我们在会话中添加更多数据而增长,并将为服务器消耗更多带宽。由于用户不断与应用程序进行交互,因此应在客户端和服务器之间经常交换此数据,从而增加成本
  2. 安全性:如果未正确实施,则可以打开安全漏洞的可能性。有人可以利用JWT验证实施的安全漏洞,并将其用来篡改会话数据,可以尝试作为管理员行事并尝试在后端访问限制资源。
  3. 陈旧的数据:我们可以在代币中维护任何可能陈旧的数据。考虑一下我们将用户偏好存储在令牌中,并且用户同时登录到多个设备。由于代币生活在客户身上。用户在一个设备客户端上所做的更改,不会在另一个设备上反映。
  4. 到期无效令牌:如何到期/无效发送给客户的会话。考虑一个情况下,用户密码已被妥协的情况。他去更改我们系统上的密码。但是黑客计算机上存在的令牌仍然没有到期。我们如何阻止该令牌访问数据?做到这一点的唯一方法是在后端保持某些状态。也许是黑名单的令牌或我们将很快讨论的解决方案。
  5. 会话大小:根据存储或交换会话数据的机制,客户/浏览器施加的限制。例如: 一个。浏览器通常支持4KB的最大曲奇大小 b。最大标头大小通常也受到限制 将请求发送到服务器,尽管可能是 修改以允许更大的标头,但仍然不 建议保存网络带宽。
在将用户会话存储在客户端时的安全注意事项

当您在客户端实施会话时,应采取适当的谨慎,以最大程度地降低安全风险并防止任何不必要的威胁和攻击。

  1. 完全避免在基于HTTP的未加密通道上交换任何敏感信息。客户端和服务器之间的完整通信应始终通过HTTPS频道进行加密。
  2. 如果我们将会话存储在cookie中,则应正确使用安全和httponly标志。有关此信息的更多信息,请找到here
  3. 我们应该在cookie上有很小的到期时间,以便它们在不活动的持续时间后自动到期。

在服务器上存储会话

由于将会话存储在客户端中的潜在安全性和漏洞问题。我们可以采用更传统的方法,将客户数据存储在后端。

Image description

  1. 用户使用其凭据登录应用程序。
  2. 服务器通过查询数据库来验证用户。
  3. 然后,服务器创建一个会话ID(随机不透明字符串),并将会话对象存储在数据库中,以使用该ID。这是一个典型的会话信息的样子:”
  4. 然后,服务器将响应标头/ cookie中的会话ID发送回客户端。
  5. 当客户端发送另一个API调用时,此会话ID将在请求中传递。
  6. 服务器获取ID并从会话存储中检索会话。
  7. 如果会话信息有效,则服务器以请求的资源响应,否则,服务器会以401错误响应。

优点

  1. 对会话数据的完全控制:由于会话属于服务器端,因此我们可以完全控制该会话。
    一个。当用户注销,更改密码或他的帐户被损害时,我们可以使数据无效。
    b。我们可以控制用户在任何给定时间点可以举行的最大活动会话的最大数量。

  2. 可以存储大量数据:在服务器上,我们可以存储相对较大的数据,并且不受浏览器或客户级级别限制的限制,例如cookie的4KB限制等。

  3. 安全:只有服务器可以通过对所需的验证进行访问和编辑用户会话数据,该数据是隐式安全的,并且可以被信任和使用。

  4. 分析:由于我们可以访问用户会话数据,因此我们可以在用户数据上运行所需的分析。

缺点

  1. 难以扩展:由于请求可以降落在后端的任何服务器上,因此我们需要维持一个可以迅速成为瓶颈的全球状态。我们可以应用不同的方案(例如粘性会话),这些方案将相同的用户请求发送到同一家服务器,但仍然没有保证。
  2. 成本:在维持全球状态方面,在后端保持状态是昂贵的。

如果您曾经使用过使用HTTP的J2EE Web应用程序工作,则必须看到JSessionID用于会话管理。服务器将JSessionId cookie创建并将客户端发送给客户端,然后在随后的HTTP请求中将其发送回服务器。

在将用户会话存储在服务器端时的安全注意事项。
  1. 会话令牌应该长时间足够随机,根本不可预测,以防止黑客使用蛮力机制。
  2. 对于每个新会话,我们也应该获得一个唯一的ID或哈希,以避免两个用户会话之间的任何冲突。
  3. 应在每个逻辑事件(例如用户登录和注销,密码重置或会话时间过期事件)中正确设置或重置会话。
  4. 如果您使用的是库或服务器管理的会话,请确保研究基本实现,跟踪新版本和修补程序更新。

混合方法

我们看到了在客户端会话中存储用户会话的两种方法,我们还可以使用某种混合方法,这些方法结合了两种方法的优势,并将尝试解决我们讨论过的任何瓶颈。

我们将使用REDIS存储会话数据,充当用于存储会话数据的全局商店。它以毫秒或更少的读写操作提供了出色的性能。

这是示例实现,我们将使用两组键值对存储在我们的redis群集上的会话信息,以实现我们的用例。

  1. sessionID =>用户ID:这将唯一的会话ID作为键,将用户ID存储为值。
  2. userId => [sessionID1,sessionID2]:这是一个redis集,它是唯一字符串(会话键)元素的集合。它非常有效,因为它提供了O(1)时间来添加,删除和测试成员的存在。每当用户登录应用程序时,它都会获得一个条目
  3. userId => sessionObject:这将唯一的会话ID作为键和实际的会话视为值。
  4. 如果我们愿意,我们仍然可以在JWT中维护其他非关键信息。

Image description

  1. 用户使用其凭据登录到该应用程序。
  2. 服务器通过查询数据库来验证用户。
  3. 服务器然后创建一个唯一的sessionID,将数据存储在redis群集中,并创建一个存储此sessionID的JWT会话令牌。
  4. 然后,服务器将授权标头中的JWT令牌或安全授权cookie发送给客户端。
  5. 当客户端发送另一个API调用时,此令牌将传递回服务器。
  6. 服务器采用JWT令牌,并使用存储的秘密验证令牌。
  7. 如果验证成功,则服务器使用JWT令牌的SessionID来从REDIS中提取信息。服务器以请求的资源响应,否则,服务器以401错误响应。

让我们看看它如何解决我们的用例。

  1. 我们拥有REDIS和JWT令牌中所需的信息。
  2. 由于会话数据存储在服务器端,因此我们可以完全控制关键会话数据。没有人可以篡改它。
  3. 如果用户重置其密码,我们可以简单地删除REDIS中的所有现有会话,用户将从所有设备中登录。
  4. 我们可以限制设备上的会话数量。

结论

我们已经了解了管理,控制和实施用户会话的不同方法。

开发人员和任何组织的管理必须非常重视会话管理,因为不正确的实施可能会导致严重的安全风险,从而有可能损害客户和组织数据的隐私和完整性。开发人员应花时间研究最佳实践,并了解其会话管理实施中可能的风险。

检查会话管理上的以下资源:
https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html

这篇文章与Redis合作。

了解更多

Try Redis Cloud for free
Watch this video on the benefits of Redis Cloud over other Redis providers
Redis Developer Hub - tools, guides, and tutorials about Redis
RedisInsight Desktop GUI