使用密码哈希作为会话 ID?

信息安全 应用安全 密码学 哈希 会话管理
2021-08-17 15:39:20

对于富客户端 Web 应用程序,我需要在服务器上验证每个呼叫是否来自合法登录的用户。显然用户 ID 是不够的,因为这很容易猜到。

我有一个我怀疑的想法,但想不出不这样做的合理理由。

使用具有足够强的散列函数(bcrypt?)保护的密码散列是一个好的会话标识符吗?它已经在数据库中,对每个用户都是唯一的,不容易猜到。我使用安全通道 (HTTPS) 进行客户端和服务器之间的所有通信,因此我不必担心会话标识符被窃听。

我不关心会话到期、状态或一次从两台计算机登录。服务器应用程序是无状态的。我真正需要的只是用户 ID 和一些额外的验证方法,以证明他已正确登录。

4个回答

这种设置可能遇到的一个问题是,根据您所说,会话令牌看起来是静态的(即,对于给定的用户,它永远不会改变,直到他们更改密码)。因此,如果攻击者设法访问给定用户的令牌(木马、键盘记录器、数据包嗅探(如果不使用 SSL)等),他们将拥有对应用程序的持久访问权限。

此外,您需要比平时更加​​小心,以确保会话令牌不会记录在服务器端的任何位置,因为它们本质上与密码一样好。

我不知道您使用哪些库来编写应用程序,但大多数都提供某种会话管理。

使用密码散列会话令牌的含义,您将无法将一个会话与另一个会话区分开来。如果两个用户有相同的密码怎么办?你能分辨出来吗?

此外,除非您选择向会话令牌添加一些随机数据,否则您将使自己容易受到重放攻击。您必须将其存储在某处的持久存储中。

当您已经走到这一步时,您应该选择生成整个会话令牌,因为您已经需要一个会话表。

对于 HTTPS 通道,只要没有人能够读取您的 cookie(记住 https 仅 cookie!)或猜测它们,任何事情都会发生。

一个好的会话 cookie 会在序列化数据之前加上一个过期时间,然后使用应用程序的密钥在过期时间 + 序列化数据前面加上一个过期时间 + 序列化数据的 HMAC。

message = serialize_to_bytes(session_data)
random = random_bytes(16)
expiration = unix_timestamp_as_16_bytes(now + 2 * 60 * 60 /*2 hours*/)
signable = random + expiration + message
signature = HMAC-SHA256(application_key, signable)
signed_message = signature + signable

signed_message是不可伪造的。它将受到重放攻击2小时。如果签名的会话 cookie 在 2 小时内被黑客入侵,会话可能会无限延长。为了做得更好,你需要想出一个好的随机数方案。在具有多个 Web 服务器提供会话 cookie 的大型应用程序中,这将需要一个包含已用随机数的中央数据库,这可能很笨拙。

一定要设置 cookie 的secure标志(这样它只在 HTTPS 请求中发送)和httponly标志(这样它就不能直接用于 JavaScript)。

请务必始终使用 SSL。

提议的方法让我觉得这是一种糟糕的方法。让 web 框架为你做会话管理。许多 Web 框架已经提供了对用户身份验证的支持,并允许您在一次调用中查询是否存在与当前请求的会话关联的登录用户。就用那个。自己滚是危险的。我不明白你为什么需要或想要为此推出一些特殊用途的机制。