如何正确地将 JTI 声明与 JWT 结合使用以防止重放攻击?

2022-09-04 22:17:24

我使用Spring创建了一些REST API,并使用JWT实现了Spring Security进行身份验证。我的前端运行 AngularJs,并使用这些静态 API 接收 JSON 响应。JWT身份验证工作正常,但它允许将请求参数和标头从浏览器的控制台简单复制和粘贴到Postman或任何其他REST客户端中,以从后端的受保护API中获取成功的响应。

我正在尝试通过在 JWT 中使用 JTI 声明来解决此问题。我计划在身份验证后为每个请求使用不同的JTI值,以便简单地从浏览器中窃取标头是行不通的。

现在,在浏览了大量在线可用资源之后,我仍然不清楚客户端或服务器是否应该在JWT中设置JTI值。

根据我的理解,如果我在服务器端执行此操作,我将不得不在每次响应时发送一个新的JWT,并在客户端的下一个请求中期望它,同时在数据库中维护已用JTI的记录。但是,如果攻击者发现了这一点,他们只需要使用来自先前请求的令牌,然后他们就可以轻松地与我的API进行交互。

另一方面,如果我在客户端执行此操作,我将不得不在javascript代码中保留JWT的秘密签名密钥和JTI生成的逻辑,以便它可以附加JTI值并再次散列令牌。我的问题是:

  1. 实现此目的的正确方法是什么?我是否错过了什么或完全走错了方向?
  2. 是否有任何其他解决方案可以禁止或取消身份验证来自非浏览器客户端的任何请求(在使用Jsps的旧版Spring MVC应用程序中发生这种情况的方式)?

任何帮助都非常感谢。已经在这个问题上呆了很长时间了。


答案 1

我不能和Java/Spring交谈,但我可以试着澄清你对JWT和JTI声明的担忧。

实现 JTI 以唯一标识 JWT 有助于防止重放攻击,攻击者发送相同的 JWT 来发出请求。服务器将生成 JTI 值,并在每次响应时将其与新的 JWT 一起发送。收到新请求时,服务器必须验证 JTI 值(以确保以前未使用过该值)。实现这一点确实需要在服务器上进行某种持久性存储,这看起来或多或少像传统会话,所以感觉有点奇怪,因为JWT的广告优势之一是“无状态应用程序”。

你对中间人攻击的担忧是绝对正确的:如果有人确实拦截了JWT(及其一次性JTI),然后在你之前发出请求,他们的请求将被视为有效,你的后续请求将在服务器中显示为“重播”(服务器会认为它们是无效的)。


答案 2

推荐