使用 JWT 的 API 客户端身份验证 — Node/TypeScript

Posted

技术标签:

【中文标题】使用 JWT 的 API 客户端身份验证 — Node/TypeScript【英文标题】:API-client authentications with JWT — Node/TypeScript 【发布时间】:2022-01-13 19:23:17 【问题描述】:

我有一个使用 node 和 TypeScript 开发的 API 和 Web 客户端。用户可以使用 JWT 进行身份验证。该系统可以正常工作,但我想知道它是否安全,或者它是否有缺陷:

系统如下:

    客户端 POST 到 API 上的 /login 端点

    在 API 上,当收到 POST /login 请求时,会生成一个 JWT 令牌,使用一些用户数据作为内容,以及一个存储为环境变量的秘密字符串:

    // SECRET is an environment variable == "bd0b2760-5869-11ec-bf63-0242ac130002"
    
    const userData = 
      id: 1,
      name: "John Doe",
    ;
    
    const token = jwt.sign(JSON.stringify(userData), SECRET);
    

    在 POST /login API 的响应中发送两个 cookie:一个持有令牌,另一个包含原始用户数据:

    return res
      .cookie('sessionData', userData, 
        httpOnly: true,
        path: '/',
        domain: "example.com",
      )
      .cookie('sessionToken', token, 
        httpOnly: true,
        path: '/',
        domain: "example.com",
      ).send();
    

    客户端收到令牌。客户端可以确定sessionToken 是有效的,因为它是由API 发送的。它不会验证它,因为它需要 SECRET,我们不想将它暴露给客户端。

    重新加载时,客户端将使用sessionData cookie 知道用户已登录,并使用此数据加载客户端用户数据。

    由于这些 cookie 是 http cookie,因此这两个 cookie 都附加到发送到 API 的每个请求中,并由 API 接收。对于需要身份验证的端点的每个请求,API 将解密 sessionToken 并将其与 sessionToken cookie 进行匹配:如果它们不匹配,API 将删除响应中的 cookie,从而有效地注销客户端。

    // SECRET is an environment variable == "bd0b2760-5869-11ec-bf63-0242ac130002"
    const sessionToken = req.cookies.sessionToken;
    const sessionData = req.cookies.sessionData;
    const decodedToken = jwt.verify(sessionToken, SECRET);
    
    if(decodedToken.id !== sessionData.id || decodedToken.name !== sessionData.name ) 
      return res
        .clearCookie('sessionToken',  path: '/', domain: "example.com" )
        .clearCookie('sessionData',  path: '/', domain: "example.com" )
    
    

如前所述,该系统有效,而且看起来很安全。但也许我错过了一些东西,所以最好问一下。所有代码都是伪代码。

欢迎任何帮助!

【问题讨论】:

它是安全的,但安全程度取决于您的用户。如果用户登录并离开他们的终端,那么有人可能会获取他们的sessionToken 并恶意使用它。 cookie 在此基础上增加了一定程度的安全性,但不是防弹的。我会推荐一个超时系统,如果用户闲置 X 时间,则将其注销。这完全取决于您的信息的敏感程度以及您所寻求的安全级别。 非常感谢@CraigHowell。实际上我已经介绍了这个案例,我的 cookie 会在 48 小时后过期。虽然没有在示例中显示它 另外,由于它们是 httpCookies,我认为不可能使用 cookie 数据发送请求,因为只允许来自特定服务器的 cookie 【参考方案1】:

它不会验证它,因为它需要 SECRET,我们不想将它暴露给客户端。

如果您希望客户端能够验证此 JWT,您可以使用非对称签名。然后客户端使用公钥,该公钥只能用于验证 JWT 是否正常。它不能用于签署新的 JWT。

重新加载时,客户端将使用 sessionData cookie 知道用户已登录,并使用此数据加载客户端用户数据。

您正在使用仅限 http 的 cookie,因此它不起作用。您的客户端无法读取该 cookie。

API 将解密 sessionToken

小事,但值得记住 - API 将 decode sessionToken cookie,而不是 decrypt。签名的 JWT 是编码的,而不是加密的。您可以创建加密的 JWT (JWE),在这种情况下,除了 API 之外没有人能够读取令牌的内容,但 JWE 更难维护。

API 将解密 sessionToken 并将其与 sessionData cookie 匹配

我不明白这如何为您提供任何额外的安全性。会话令牌是一个签名的 JWT,因此您可以轻松验证它以检查它是否被篡改。如果有人设法窃取了 sessionToken cookie,他们还不如窃取了 sessionData cookie 并将它们一起使用。您正在使用仅 http cookie,因此 XSS 将无法窃取这些 cookie 的内容。您还可以使用 secure 标志来确保这些 cookie 不会通过未加密的连接发送。

【讨论】:

以上是关于使用 JWT 的 API 客户端身份验证 — Node/TypeScript的主要内容,如果未能解决你的问题,请参考以下文章

使用 IdentityServer 与创建基于 JWT 的自定义身份验证

如何将 JWT 从客户端传递到我的 API 以进行正确身份验证?

如何跨不同 API 对 JSON Web 令牌 (JWT) 进行身份验证?

这种基于 JWT 的身份验证方法安全吗?

具有混合身份验证 JWT 和 SAML 的 ASP.NET Web API 2.2 OWIN

如何使用葡萄 gem 和 jwt 作为身份验证构建第三方 API