Openid 连接 nonce 重放攻击

信息安全 随机数 开放式连接
2021-08-21 00:09:42

openid 连接规范向授权端点添加了一个 nonce 参数,该参数必须作为 id_token 中的声明回显。它声称这个参数的目的是防止重放攻击,并且有一些关于使用 http only cookie 的实现建议。

我了解一般的重放攻击是什么,但我想更好地了解可以防止的重放攻击的详细信息,因此我可以更好地了解我是否有效地实施了它,或者犯了一些微妙的错误。

我假设重播攻击是针对授权端点的,以便某些第三方获取与不同用户相对应的令牌。是它还是别的什么?我们期望攻击者必须重播什么级别的信息?他们是否有用户代理和授权服务器之间的流量,但没有客户端应用程序,这就是为什么他们能够将流量重播到授权端点但不能将之前的 cookie 重播到客户端应用程序?或者他们有两者的浏览器历史但没有网络流量,所以他们没有旧的 cookie 值?用户代理是否还有以前的用户有关系吗?重放发生时与授权服务器的会话?如果前一个用户确实与授权服务器有一个有效的会话并且攻击者可以访问它,那么他们不能在不进行重放的情况下访问令牌吗?如果他们没有与授权服务器的有效会话,即使他们重放旧请求,用户也不会被提示?如果将提示用户进行身份验证,可能使用他们没有的凭据,那么重放的危险是什么?

我已经做了很多搜索,并且有一些类似的问题(Replay attack example for validating nonce? and Purpose of nonce validation in OpenID Connect implicit flow)实际上都没有回答有关这种特定重放攻击如何工作的问题。第一个只是从规范中引用了推荐的实现,然后谈到了泄露的令牌,这在我看来与重放攻击不同(如果攻击者有泄露的令牌,他们不是已经赢了吗?)。第二部分解释了重放攻击的一般概念,但没有讨论这种特定攻击的实际工作原理。

作为一些背景,我正在使用授权代码流制作一个 openid 连接应用程序(因此重播从 AS 到我的应用程序的重定向不应该做任何事情,因为您只能在 AFAICT 一次为令牌交换相同的授权代码,所以你会还需要重放授权端点以使重放攻击有意义。)我的应用程序正在使用我作为承载令牌返回的令牌来验证对我的 Web 服务器的请求,它们是整个会话状态,没有其他有意义的会话状态或本地管理的身份来绑定令牌。我的 openid 连接提供程序需要一个随机数,因为它是一种安全最佳实践。它在我的特定用例中真的有用吗?是我如此困惑的原因,因为实际上并没有为我的特定事物提供有意义的保护' 我在做什么?如果这确实对我有安全影响,我 110% 想确保我做正确的事,但我很难理解它保护我免受什么伤害。

由于不完全了解威胁向量,一些实现细节问题我很模糊:

  • 我是否验证随机数与从令牌端点拉回后发送到授权端点的内容相匹配,然后假设它对会话有益?还是应该像检查签名一样,根据 nonce cookie 验证 id_token cookie 作为每个请求的一部分?
  • nonce 应该与会话绑定,但术语 nonce 意味着它应该只使用一次。如果出于某种原因我需要通过授权端点将具有有效会话的用户发送回(获取一个新的 id_token 并在未来进一步到期?请求更广泛的范围?)我是否创建一个新的随机数?每次会话或每次调用授权一个随机数?
2个回答

阅读您的问题,我相信混乱源于重放攻击发生的位置。nonce 防止针对客户端(您的应用程序)而不是授权服务器的重放攻击。

我了解您使用的是代码流,但为了简单起见,我们假设您在单页应用程序中使用隐式流并且您没有后端服务器(例如,没有服务器端会话) . 这是随机数有助于缓解的攻击向量:

  1. 客户端应用程序将用户代理重定向到response_type具有id_token.
  2. 用户与认证服务器建立身份,即登录。
  3. 身份验证服务器将用户代理重定向回客户端应用程序,并使用id_token. 响应如下所示:https://your-single-page.app/auth#token_type=bearer&state=some-state&id_token=some-token
  4. 攻击者可能通过数据包嗅探、客户端服务器日志(例如,托管您的 SPA 的 Web 服务器)、浏览器的开发人员工具、肩冲浪或其他方式获得该响应。

nonce 有助于防止攻击者获取授权服务器的响应,将其粘贴到他们的 URL 栏中,并与您的客户端应用程序建立身份。就是这样:

  1. 客户端应用程序生成一个安全的随机 nonce,并将其以明文、cookie、会话存储或持久性的方式存储。
  2. 客户端应用程序对该随机数进行散列并将散列作为身份验证请求参数发送。
  3. 当客户端应用程序处理身份验证响应时,它会从持久存储中提取并删除 nonce,对其进行哈希处理,并将其与id_token. 如果它们不匹配,则客户端应用程序拒绝建立身份。

攻击者可能已经截获了响应,包括id_token,但这里的 nonce 有效地充当客户端应用程序的密码。攻击者需要明文随机数来直接与客户端应用程序建立身份。(我说“直接”是因为,根据应用程序,攻击者可能能够绕过随机数检查。)需要明确的是,获得授权响应只会让攻击者访问随机数的哈希,而不是实际的随机数。

使用代码流时存在相同的攻击向量,尽管更难成功利用。例如,假设攻击者拦截了身份验证响应然后,攻击者可以将响应(302 位置)粘贴到他们的 URL 栏中,并让您的客户端应用程序发出令牌请求。当授权服务器响应时,您的客户端应用程序可以根据您的服务器与用户的用户代理绑定的内容(例如,存储在仅 HTTP cookie 中的加密随机值)来验证 ID 令牌中的随机数。同样,nonce 充当客户端应用程序的密码. 我要指出,要成功利用这个确切的攻击向量,假设授权服务器不会验证授权代码是否已被使用。这在规范中是可选的:“如果可能,请验证之前未使用过授权码。”

在我看来,如果授权服务器阻止授权代码被重用(它应该),那么在使用代码流时很难利用这种类型的重放攻击。但是,如果可以访问身份验证响应,则使用隐式流重放 ID 令牌是微不足道的。因此,nonce 在代码流中是可选的,在隐式流中是必需的。

由于在这个答案中多次提到了隐式流,因此值得指出的是,OAuth Group建议不要使用它,因为它存在许多安全风险,“并非所有这些都有足够的缓解策略”。应该改用带有 PKCE 的代码流。

有问题的 nonce 旨在与创建 OpenID 令牌时默认超出范围的 HTTP 会话相关。通过 HTTP 会话数据不用于链接 OpenID 令牌,这允许理论上的攻击者从网络中嗅探 Open_ID 令牌并尝试针对授权服务器“重放”凭据以进行访问。这将成功,因为默认情况下,除了令牌内的签名之外,OpenID IDP 没有给定的方法来识别源。

为了解决这个问题,OpenID 允许授权服务器利用此端点身份验证随机数来组合 HTTP 会话 cookie,该 cookie 仅在服务器/客户端级别作为哈希值已知。假设会话 id 是唯一的并且每个会话随机生成,这会给潜在攻击者带来问题。当攻击者试图重放凭据时,他们必须猜测原始用户拥有的 HTTP 会话的哈希值。

阅读来自 OpenID 的注释会很有帮助:

https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes

最大的因素应该是:

  • NONCE 值应基于安全生成的随机字符串
  • NONCE 值不应该被使用两次
  • NONCE 值应该与服务器对客户端的了解相关联并且可以保密。

我希望这有帮助。