#tokens #session #session-token #signatures #signature

signed_tokens

创建和验证适用于认证会话ID的数字签名令牌

1个不稳定版本

0.2.0 2024年7月10日

#858 in 加密学

Download history 86/week @ 2024-07-05 23/week @ 2024-07-12 1/week @ 2024-07-19 3/week @ 2024-07-26

每月110次下载

MIT许可协议

21KB
248

签名令牌

CI

一个用于创建和验证HMAC签名令牌的简单Rust包,具有多个可旋转密钥。

此包的典型用例是认证会话令牌。用户成功登录后,您的系统应

  1. 生成一个随机会话ID,例如使用uuid包
  2. 使用会话ID作为键将账户信息放入缓存中(例如Redis)
  3. 使用此包对会话ID进行数字签名并对其进行base64编码
  4. 将签名令牌包含为一个安全的、仅HTTP cookie

在后续请求中,使用此包验证签名令牌并检索会话ID,以便您可以在缓存中查找会话数据。

用法

用户成功登录后,为会话ID生成一个随机值——UUID非常适合此目的,但您可以使用任何唯一值

use uuid::Uuid;

let session_id = Uuid::new_v4().to_string();

要数字签名会话ID,您还需要一些密钥。您的服务器是唯一需要知道这些密钥的,但它们在服务器重启后应保持不变,以便现有会话仍然有效。您可以从环境变量中读取它们,或从文件中读取,或从密钥管理服务中读取,或 whatever。但它们应该对您的服务器保密,永远不要直接添加到源代码中。

use signed_tokens::SigningKey;

let signing_keys = vec![
    SigningKey::new(env::var("SESSION_SIGNING_KEY_1").unwrap()),
    SigningKey::new(env::var("SESSION_SIGNING_KEY_2").unwrap()),
    SigningKey::new(env::var("SESSION_SIGNING_KEY_3").unwrap()),
];

您可以有最多255个签名密钥。每个密钥都有一个status,默认为SigningKeyStatus::SignAndVerify。当您想停止使用密钥签名新令牌,但允许使用它验证现有令牌时,可以将其更改为VerifyOnly

当您签名会话ID时,此包将随机选择一个签名和验证密钥。所选密钥的切片索引将被添加到签名和编码的令牌中,以便库知道在以后验证令牌时使用哪个密钥。

要签名您的会话ID,请将它们和您的签名密钥切片传递给sign()函数

let token = signed_tokens::sign(&session_id, &signing_keys)?;
let url_safe_base64_token = token.to_string();

《sign》方法返回一个包含所选签名密钥索引、负载(在此情况下为您的会话ID)和HMAC签名的二进制缓冲区的《SignedToken》结构体。可以使用《to_string》方法将其转换为base64编码的字符串。

当您向客户端响应时,将base64编码的字符串作为安全的HttpOnly cookie包含在内。当此cookie在后续请求中返回时,使用《verify》函数进行验证。

// Use the same set of signing keys as you did when signing
let verified_token = signed_tokens::verify(&token_from_request_cookie, &signing_keys)?;
let session_id = verified_token.payload();

// look up account info in your cache using `session_id`...

从《verify》函数返回的《VerifiedToken》不仅包含您在签名令牌时传递的负载,还包含用于验证令牌的《SigningKeyStatus》。这有助于在一段时间内轮换密钥(有关更多详细信息,请参阅下一节)。

如果以下任何情况发生,《verify》函数将返回一个错误《Result》:

  • 令牌已被篡改,即自签名以来负载或签名已更改
  • 令牌包含一个不再在提供的签名密钥数组中的签名密钥索引
  • 令牌不是一个有效的base64字符串
  • 令牌太短,无法作为有效的令牌

随着时间的推移轮换密钥

随着时间的推移轮换您的签名密钥是个好主意,即使它们从未被损害。为了在不中断您的客户端的情况下进行轮换,请按照以下步骤操作

  1. 将您要替换的密钥的《status》更改为《VerifyOnly》。这将使该密钥过时,因此现有的令牌仍然可以验证,但不会使用该密钥签署任何新的令牌。
  2. 在成功验证后,如果《VerifiedToken.key_status》方法返回《SigningKeyStatus::VerifyOnly》,请再次调用《sign》方法,将《VerifiedToken》中的负载作为参数传递,以使用活动签名密钥生成新的《SignedToken》。然后,您可以将此新签名令牌包含在响应中,作为会话cookie的新值。客户端将在后续请求中向您的服务器发送此新令牌。
  3. 经过足够长的时间后,用新的活动密钥替换过时的《SigningKey》。如果客户端之前使用过时密钥签名的令牌,并且在它过时后从未返回您的网站,那么该客户端的令牌将不再验证。客户端只需重新登录即可获得新的会话令牌。

依赖关系

~1-1.5MB
~33K SLoC