OAuth 2如何使用安全令牌防止重放攻击?

根据我的理解,OAuth 2中发生了以下事件链,以便Site-ASite-B访问用户的信息。

  1. Site-ASite-B上注册,并获得Secret和ID。
  2. 用户告诉Site-A访问Site-B时,用户被发送到Site-B,在那里他们告诉Site-B他们确实想给Site-A特定信息的权限。
  3. Site-B用户重定向回Site-A,并附带一个授权代码。
  4. Site-A然后将授权代码及其秘密传递回Site-B,以换取安全令牌。
  5. 然后,Site-A通过将安全令牌与请求捆绑在一起,代表用户Site-B发出请求。

在高水平上,所有这些在安全和加密方面是如何工作的?OAuth 2如何使用安全令牌防止重放攻击?

246823 次浏览

根据我所读到的,它是这样运作的:

问题中概括的大致流程是正确的。在步骤2中,用户X经过身份验证,并授权站点A访问站点B上的用户X的信息。在步骤4中,站点将其Secret传递回站点B,验证自身以及授权码,表明它正在请求什么(用户X的访问令牌)。

总的来说,OAuth 2实际上是一个非常简单的安全模型,加密从未直接发挥作用。相反,Secret和Security Token本质上都是密码,整个事情仅由https连接的安全性保护。

OAuth 2没有针对安全令牌或秘密的重放攻击的保护。相反,它完全依赖于站点B负责这些项目,不让它们流出,并在传输过程中通过https发送(https将保护URL参数)。

授权码步骤的目的只是为了方便,而授权码本身并不是特别敏感。当向站点B请求用户X的访问令牌时,它为站点a的用户X的访问令牌提供了一个公共标识符。仅在站点B上使用用户X的用户id是行不通的,因为可能有许多未完成的访问令牌等待同时分发到不同的站点。

另一个答案非常详细,解决了OP提出的大部分问题。

为了详细说明,并具体解决OP的问题“OAuth 2如何防止使用安全令牌的重放攻击?”,在实现 OAuth 2的官方建议中有两个额外的保护措施:

  1. 代币通常有一个很短的有效期(https://www.rfc-editor.org/rfc/rfc6819#section-5.1.5.3):
令牌的短到期时间是一种保护手段 以下威胁:

  • 重播…
  1. 当站点A使用令牌时,建议它不作为URL参数,而是在授权请求报头字段(https://www.rfc-editor.org/rfc/rfc6750)中显示:
客户端应该使用承载令牌进行身份验证请求 “Authorization"请求头字段中带有“Bearer"HTTP 授权方案。 …< / p > < p >“应用程序/ x-www-form-urlencoded"方法不应使用 除非在应用程序上下文中,其中参与的浏览器不这样做 有权获得“授权”;请求报头字段。 …< / p >

URI查询参数…用于记录当前使用情况;它的用途不是 推荐,由于其安全性缺陷

OAuth是一种协议,第三方应用程序可以在不需要您的帐户和密码的情况下访问存储在另一个网站上的数据。有关更正式的定义,请参考Wiki或规范。

下面是一个用例演示:

  1. 我登录了领英,想要联系我Gmail联系人中的一些朋友。LinkedIn支持这一点。它将从gmail请求一个安全资源(我的gmail联系人列表)。我点击这个按钮:
    Add Connection

  2. 弹出一个web页面,它显示Gmail登录页面,当我输入我的帐户和密码:
    Add Connection

  3. Gmail然后显示一个同意页面,我点击“接受”: Add Connection

  4. 现在LinkedIn可以访问我在Gmail的联系人: Add Connection

下面是上面例子的流程图:

Add Connection

步骤1:LinkedIn从Gmail的授权服务器请求一个令牌。

步骤2:Gmail授权服务器对资源所有者进行身份验证,并向用户显示同意页面。(如果用户尚未登录,则需要登录Gmail)

步骤3:用户授予LinkedIn访问Gmail数据的请求。

步骤4:Gmail授权服务器返回一个访问令牌。

步骤5:LinkedIn使用这个访问令牌调用Gmail API。

步骤6:如果访问令牌有效,Gmail资源服务器将返回您的联系人。(令牌将由Gmail资源服务器验证)

您可以从OAuth 在这里获得更多详细信息。

OAuth 2.0在现实生活中的工作原理:

我开车去上班的路上经过奥拉夫面包店,看到橱窗里最好吃的甜甜圈——我的意思是,那东西滴着巧克力般的美味。所以我走了进去,要求“我必须要那个甜甜圈!”他说:“当然是30美元。”

我知道,30美元一个甜甜圈!一定很好吃!我伸手去拿钱包,突然听到厨师喊道:“不!你没有甜甜圈。”我问:为什么?他说他只接受银行转账。

严重吗?是的,他是认真的。我差点就走开了,但这时甜甜圈对我喊道:“吃了我,我很好吃……”我凭什么违抗甜甜圈的命令?我说好的。

他递给我一张纸条,上面写着他的名字(是厨师,不是甜甜圈):“告诉他们奥拉夫让你来的。”他的名字已经写在纸条上了,所以我不知道这么说有什么意义,不过好吧。

我开了一个半小时的车去银行。我把钞票递给出纳员。我告诉她奥拉夫派我来的。她看了我一眼,好像在说:“我识字。”

她拿着我的纸条,问我要身份证,问我可以给他多少钱。我告诉她30美元。她草草写了几笔,又递给我一张纸条。这个上面有一堆数字,我猜这是他们记录音符的方法。

那时我饿坏了。我冲了出去,一个半小时后我回来了,站在奥拉夫面前,手里拿着我的纸条。他拿了过来,看了看,说:“我会回来的。”

我以为他在买我的甜甜圈,但30分钟后我开始怀疑了。所以我问柜台后面的人“奥拉夫在哪里?”他说"他去拿钱了"“你这是什么意思?”“他把票据送到银行去了。”

嗯……于是奥拉夫拿着银行给我的条子,回到银行去从我的账户里取钱。因为他有银行给我的票据,银行知道他就是我说的那个人,而且因为我和银行谈过,他们知道只给他30美元。

我一定花了很长时间才弄明白,因为当我抬起头的时候,奥拉夫已经站在我面前,把我的甜甜圈递给我。在我离开之前,我不得不问:“奥拉夫,你总是这样卖甜甜圈吗?”“不,我以前做的不一样。”

嗯。当我走回我的车时,我的电话响了。我没去接,可能是我的工作让我开除的,我的老板真是个***。此外,我一直在想我刚刚经历的过程。

我的意思是,想想看:我可以让奥拉夫从我的银行账户中取出30美元,而不必把我的账户信息告诉他。我也不用担心他会取太多的钱,因为我已经告诉银行他只能取30美元。银行知道他是合适的人因为他有他们让我交给奥拉夫的纸条。

好吧,我宁愿从口袋里掏出30美元给他。但现在他有了那张钞票,我可以告诉银行让他每周拿30美元,然后我就可以去面包店了,再也不用去银行了。如果我愿意,我甚至可以通过电话订购甜甜圈。

我当然不会那么做,那个甜甜圈太恶心了。

我想知道这种方法是否有更广泛的应用。他提到这是他的第二种方法,我可以称之为Olaf 2.0。总之我得回家了,我得开始找新工作了。但在我从镇另一头的新店里买到草莓奶昔之前,我需要一些东西来洗掉甜甜圈的味道。

图1,摘自RFC6750:

     +--------+                               +---------------+
|        |--(A)- Authorization Request ->|   Resource    |
|        |                               |     Owner     |
|        |<-(B)-- Authorization Grant ---|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(C)-- Authorization Grant -->| Authorization |
| Client |                               |     Server    |
|        |<-(D)----- Access Token -------|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(E)----- Access Token ------>|    Resource   |
|        |                               |     Server    |
|        |<-(F)--- Protected Resource ---|               |
+--------+                               +---------------+

这就是Oauth 2.0的工作原理,在这篇文章中有很好的解释

enter image description here

这是一块宝石:

https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2

非常简短的总结:

OAuth定义了四个角色:

  1. 资源所有者
  2. 客户端
  3. 资源服务器
  4. 授权服务器

您(资源所有者)有一部手机。你有几个不同的电子邮件帐户,但你希望所有的电子邮件帐户都在一个应用程序中,所以你不需要一直切换。因此,您的GMail(客户端)要求访问(通过雅虎的授权服务器)到您的雅虎电子邮件(资源服务器),以便您可以在GMail应用程序上阅读这两封电子邮件。

OAuth存在的原因是GMail存储您的Yahoo用户名和密码是不安全的。

enter image description here

这里可能是OAuth2如何为所有4种授权类型工作的最简单的解释,即应用程序可以获得访问令牌的4个不同的流。

相似

所有授权类型流都有两部分:

  • 获取访问令牌
  • 使用访问令牌

第二部分“使用访问令牌”对于所有流都是相同的

区别

每个拨款类型的流“获取访问令牌”的第一部分是不同的。

然而,总的来说,“获取访问令牌”部分可以概括为5个步骤:

  1. 预先注册你的应用(客户端)与OAuth提供商,例如,Twitter等,以获得客户端id/秘密
  2. 创建一个社交登录按钮与客户端id &需要的范围/权限在您的页面,以便当点击用户重定向到OAuth提供者进行身份验证
  3. OAuth提供商请求用户授予你的应用(客户端)权限
  4. OAuth提供者发布代码
  5. App(客户端)获取访问令牌

下面是一个并排的图表,比较了基于5个步骤的每个拨款类型流程的不同。

这个图表来自https://blog.oauth.io/introduction-oauth2-flow-diagrams/

enter image description here

每一个都有不同的实现难度、安全性和用例级别。根据您的需要和情况,您将不得不使用其中之一。用哪一种?

客户端凭证:如果你的应用只服务于一个用户

资源所有者密码凭证:这应该仅作为最后的手段,因为用户必须将他的凭据交给应用程序,这意味着应用程序可以做任何用户可以做的事情

授权代码:获得用户授权的最佳方式

隐式的:如果你的应用是手机应用或单页面应用

这里有更多关于选择的解释:https://blog.oauth.io/choose-oauth2-flow-grant-types-for-app/

老实说,我在答案中没有找到一个回答“OAuth 2如何使用安全令牌保护像重放攻击这样的问题?”的答案,这是一个主要的问题。

首先,OP描述的访问方案只适用于OAuth 2.0提供的流之一- 授权代码授予。还有其他的流动。所有流的一个共同特征是,作为成功身份验证的结果,客户端接收一个访问令牌

如何保护自己免受重放攻击?这是可能的(有一些保留意见),但您需要理解,首先,这需要一组措施(如下所述),其次,您不能100%地保护自己免受这种类型的攻击,有时您可以立即阻止未经授权的访问尝试,有时您只能在发生此类攻击时缩短持续时间。

所以你需要做什么:

  1. 使用带符号的JWT作为您的令牌。
  2. 对访问令牌使用非常短的过期时间,在我看来10分钟就足够了。
  3. 您的授权服务器必须发出刷新令牌,通常是可根据标准选配。刷新令牌的过期时间不应该太长,对于每种情况都应该有不同的解决方法,例如,对于一个网站,我会把它设置得比正常的用户会话长一点。您还可以在用户空闲时实现会话过期,但这适用于应用程序逻辑,并且不是标准所提供的(这是一种相当简单的机制,但超出了问题的范围)。
  4. 必须将已发出的刷新令牌存储在授权服务器数据库中。但是,您不必存储访问令牌数据来利用自包含的jwt。
  5. 建议在会话的生命周期内存储关于刷新令牌的数据,也就是说,直到刷新令牌过期为止(实际上,它将不是一个令牌,而是一个家族——下面将详细介绍)。
  6. 采取一般措施来防止令牌/会话盗窃,这些措施可能是众所周知的,其中包括:只使用安全连接;如果您使用cookie在最终用户端存储令牌,请设置cookie标志来保护它们,详情请点击这里;实现对跨站请求伪造(CSRF) 详情请点击这里的保护。
  7. 实现刷新令牌旋转。这意味着每次客户端使用刷新令牌来获得一个新的访问令牌(因为访问令牌已经过期),新的刷新令牌必须与新的访问令牌一起发出,旧的刷新令牌必须失效。它可能只是数据库中的一个标志,表明刷新令牌无效。
  8. 每次授权服务器发出一个刷新令牌时,它必须向它添加以下声明(在其他必需/推荐的声明中):带有唯一令牌id的jti和带有任何fid4的私有声明,例如带有唯一令牌族id的fid(在一个会话中)。例如,refresh token 1jti 3c30a712-247b-4091-b692-8c3e92b83bb2fid 4eb44450-84e9-4fbc-830e-33935e20f7e6,在发布refresh token 2而不是refresh token 1之后,它可能有一个新的jti fid0,但将有相同的fid 4eb44450-84e9-4fbc-830e-33935e20f7e6。在数据库中保持整个刷新令牌家族,直到最后一个仍然有效的令牌失效,例如,直到它过期为止。*您可以不使用fid声明,那么您将不得不使用关系数据库机制链接同一会话中发出的整个刷新令牌链/家族。
  9. 为刷新令牌实现绝对过期时间。每次,当同一会话中的授权服务器发出一个新的刷新令牌而不是以前的刷新令牌时,它的exp声明的值不应该超过第一个刷新令牌的过期时间。例如,如果refresh token 1对于exp声明的值为1643384057,那么每个后续刷新令牌(例如refresh token 5)也应该在exp声明中包含相同的值1643384057
  10. 实现刷新令牌重放(重用)检测。也许你已经猜到接下来要做什么了。每当授权服务器接收到发出访问令牌的请求时,授权服务器(除其他外)必须检查所提供的刷新令牌是否来自现有链/族,并且没有标记为无效。# EYZ0

当攻击者窃取令牌/会话并试图重用它时会发生什么。有以下几种情况:

  1. 攻击者使用了令牌/会话,在合法用户的请求下,客户端请求发出新的访问和刷新令牌。也就是说,攻击者设法先做到这一点。然后,在合法用户的下一个请求时,客户端将向授权服务器发送一个无效的刷新令牌(因为攻击者较早地发出了请求,并且合法用户的刷新令牌无效)。会话将失效。
  2. 令牌/会话被合法用户使用,被盗的令牌/会话后来被攻击者使用。在这种情况下,同样的事情会发生-会话将无效,我认为这是可以理解的。
  3. 有可能在令牌/会话被窃取后,合法用户没有再发送任何请求,那么攻击者将拥有访问权限,直到刷新令牌的绝对过期(参见第9点)。

授权服务器无法知道谁是合法用户,谁是攻击者,因此在这种情况下,最后一个(有效的)刷新令牌总是无效的,从而使会话过期/无效。在此之后,合法用户可以通过输入密码来验证自己的身份,而攻击者则不能。

了解这是如何工作的,您应该选择与令牌过期相关的值,这些值与您的项目相关。

我建议您深入研究相关标准OAuth 2.0安全最佳当前实践。在那里你还会找到令牌重放预防部分

OAuth2本身并不能保护您免受重放攻击。然而,有“扩展”;比如MTLS或DPoP。你可以在https://marcinjahn.com/technologies/security/oauth2/sender-constraint.html上找到更多信息