这是创建和验证会话令牌的正确技术吗?

信息安全 会话管理
2021-08-17 01:46:36

当前令牌格式、创建、验证:

vls_k3uGjFsDfA49Ygt8mqNHAtkBuUqRTU6K1KfUCwEiX9Z

我正在创建会话令牌,如下所示:

  1. 创建一个 32 字节的数组。

  2. 通过 PRNG 填充前 28 个字节。

  3. 从 28 个字节计算校验和 (CRC-32),并将结果添加到数组的末尾。

  4. 使用 Base-58 对数组进行编码并附加前缀 (vls_)

令牌如何存储在数据库中:

  1. 通过 (SHA-512) 散列创建的令牌

  2. 将其存储在具有关联用户 ID 的表中

我正在验证令牌如下:

  1. 检查它是否有前缀。

  2. 切片它以获得没有前缀的令牌

  3. 对其进行解码以获取字节。

  4. 再次将其切片为随机字节(字节数组长度 - 4)并计算校验和

  5. 将令牌校验和与计算的校验和进行比较

  6. 散列令牌 (SHA-512)

  7. 检查数据库...

为了清楚起见,跳过了一些检查。

我的问题:

使用 SHA-512 而不是 SHA-256 我能得到什么吗?

  • 我认为不是因为令牌已经是高熵。

  • 如果攻击者以某种方式获得了表格并且可能会知道格式,那么他就可以生成令牌和散列,那么使用什么散列方法就无关紧要了。

我可能完全错了,所以我愿意接受任何反馈。

1个回答

这种方法是过度设计的。你不需要校验和。

您有一个数据库,您可以在其中存储与用户关联的会话 ID。使用加密安全随机数生成器 (CSPRNG) 生成随机会话 ID。16 字节(128 位)的长度就可以了。如果需要将其表示为字符串,请将其格式化为十六进制或 base64。为用户提供会话 ID,例如作为 cookie。散列会话 ID,然后将其与会话的到期日期(时间戳)一起存储在数据库中。

当用户发送请求时,获取他们发送的会话 ID,对其进行散列,然后在数据库中查找该散列。如果找到散列会话 ID,并且当前时间戳不超过数据库中的到期日期,则会话有效并且他们已登录。否则他们没有登录。您可以在每个成功的请求,使其仅在一段时间不活动后过期。

您不需要对提供的令牌进行格式和长度验证,但为了良好的做法,您至少应该检查它的长度是否正确并且只包含预期的字符(例如,/^[0-9a-f]{32}$/如果它是编码为十六进制的 128 位值,则为正则表达式细绳)。您不需要任何前缀或校验和。如果用户更改该值,它将与会话 ID 不匹配。

您还可以选择存储用户的 IP 地址和浏览器用户代理等信息,并在尝试使用具有不同 IP/代理的会话 ID 时拒绝访问。这有助于防止从浏览器中窃取会话 ID,例如,如果用户安装了恶意软件。

在数据库中对会话 ID 进行散列处理的原因是,如果攻击者获得对数据库的读取访问权限(例如通过 SQL 注入),则攻击者无法轻易接管活动用户会话。因为会话 ID 是一个很长的随机字符串,所以您不需要在此处使用加密散列(例如 SHA256)以外的任何内容。哈希不需要加盐,也不需要像 Argon2、bcrypt 或 PBKDF2 这样的计算难的 KDF。攻击者无法猜测由 CSPRNG 生成的足够长(即 128 位或更大)的值,并且密钥空间对于对散列的暴力攻击来说太大了。

对会话 ID 进行散列处理的另一个原因是对索引查找的计时攻击。索引查找在搜索字符串时不采用恒定时间路径,因此这种定时侧通道可能允许攻击者逐步减少搜索空间并发现活动会话 ID。这种攻击在实验室场景之外通常是不切实际的,但解决方案是对令牌进行哈希处理,以便攻击者无法轻松地使用选定的前缀执行数据库索引查找。由于其他原因,我们已经在对会话 ID 进行哈希处理,因此实用性问题没有实际意义。

哈希函数的输出长度在这里并不重要。128 位会话 ID 绰绰有余,因此只要您使用输出大小大于最小安全界限(128 位)的加密哈希函数就可以了。使用 SHA512 代替 SHA256 没有任何好处,只会增加哈希的存储大小和计算成本。

您必须使用 CSPRNG 来生成会话 ID。标准库随机函数喜欢rand()mt_rand()不适合生成安全敏感的秘密。您的语言的标准库可能会提供加密随机数生成器 API,例如random_bytesPHP、RandomNumberGenerator.NET 或SecureRandomJava。您也可以在 Linux/BSD 环境中阅读/dev/urandom(但不是/dev/random- 这是一个遗留界面)。