正如 Jacco 所解释的,在 oauth2 之上的简单身份验证实现有几个漏洞,其中最常见的是CSRF。
鉴于有一个完美的身份验证协议可用而没有所有这些陷阱,因此推出自己的身份验证协议并不是一个好主意。
OTOH,通过这样做以及理解和解决这些问题,可以学到很多东西。
TL;DR:除非您这样做是为了了解为什么不应该这样做,否则不要使用 oauth2 进行身份验证。使用 OpenID 连接。
#OAuth 2.0 威胁模型和安全注意事项
首先,RFC6819中对 oauth2 的威胁模型进行了广泛的分析
oauth2 中有几种可能的“流程”。我为我的项目关注的一个是authorization_code
流程。
#授权“代码”
以下是 RFC6819 对此的说明:
授权“代码”代表成功的最终用户授权过程的中间结果,客户端使用它来获取访问和刷新令牌。授权“代码”被发送到客户端的重定向 URI 而不是令牌,用于两个目的:
基于浏览器的流通过 URI 查询参数(HTTP 引用者)、浏览器缓存或日志文件条目向潜在攻击者公开协议参数,并且可以重放。为了减少这种威胁,短暂的授权“代码”被传递而不是令牌,并通过客户端和授权服务器之间更安全的直接连接交换令牌。
在客户端和授权服务器之间的直接请求期间对客户端进行身份验证比在间接授权请求的上下文中简单得多。后者需要数字签名。
所以授权码更安全,耶!
authorization_code
流漏洞在RFC6819的4.4.1 节中进行了分析。
本节涵盖了很多内容。我将只关注一些威胁。
##CSRF
从第 4.4.1.8 节开始:
攻击者可以将授权“代码”授权给授权服务器上他们自己的受保护资源。然后,他在他的设备上中止重定向流回客户端,并诱使受害者执行重定向回客户端。客户端接收重定向,从授权服务器获取令牌,并将受害者的客户端会话与使用令牌可访问的资源相关联。
影响:用户代表攻击者访问资源。[...] 例如,用户可能会将私人项目上传到攻击者的资源
这也包含在RFC6749 的第 10.12 节中:
客户端必须为其重定向 URI 实现 CSRF 保护。这通常通过要求发送到重定向 URI 端点的任何请求包含一个将请求绑定到用户代理的已验证状态的值(例如,用于验证用户代理的会话 cookie 的哈希)来实现。客户端在发出授权请求时应该使用“state”请求参数将此值传递给授权服务器。
因此,在重定向到 oauth2 提供程序时,您只需添加一个参数state
,它只是一个 CSRF 令牌(应该是不可猜测的,存储在安全的 cookie 中等)。authorization_code
当 oauth2 提供程序将用户重定向回来时,此令牌将与发送回。
这种攻击的对策必须由客户端和授权服务器共同实施,也可以由授权服务器强制执行。
state 参数也包含在这个 sec.SE question 中。
##代码替换(OAuth登录)
这个(在 RFC6819 的第 4.4.1.13 节中介绍)专门针对 oauth2 场景的身份验证。
基本上,攻击者authorization_code
通过恶意站点(我们称其为站点 C)为用户获取一个,并将其发送到合法站点(我们仍将其称为站点 A),该站点将其交换为一个 access_token,然后用于断言用户的身份通过资源服务器。这有效地让攻击者以站点 A 上的用户身份登录。
这是 Jacco 在他的回答中提到的。
这种攻击的对策必须由授权服务器实施:
所有客户端都必须在每个请求中指明其客户端 ID,以交换访问令牌的授权“代码”。授权服务器必须验证特定的授权“代码”是否已发给特定的客户端。如果可能,应事先对客户端进行身份验证。
其他
信不信由你,之前的攻击及其对策涵盖了使用代码流时对身份验证的大部分威胁。
还有许多其他威胁和对策,其中许多应始终实施:
从第 4.4.1.3 节开始:
基于句柄的令牌必须使用高熵对客户端进行身份验证;这增加了攻击者必须猜测的另一个值 将授权“代码”绑定到重定向 URI;这增加了攻击者必须猜测的另一个值 对令牌使用较短的到期时间
这些都应该由授权服务器实现。
从第 4.1.1.4 节开始:
授权服务器应验证客户端 授权服务器应根据预先注册的重定向 URI 验证客户端的重定向 URI
这些也应该由授权服务器实现。
从第 4.4.1.5、4.4.1.6 和其他部分:
客户端的重定向 URI 应指向受 HTTPS 保护的端点
这应该由客户端实现,并且可能由授权服务器强制执行。
#那么就可以使用oauth2登录了
不。不要这样做。使用 OpenID 连接。
还记得第 4.4.1.13 节的对策吗?好吧,还有一个我没有引用:
客户端应使用适当的协议,例如 OpenID (cf. [OPENID]) 或 SAML (cf. [OASIS.sstc-saml-bindings-1.1]) 来实现用户登录。两者都支持对客户端的受众限制。
你去吧。改用那个。
如果您仍然希望/需要针对 oauth2 提供者进行身份验证,请首先确保您的提供者实施上述所有对策。
如果确实如此,那么您也许可以将其拉下来。广泛测试并聘请安全团队对您的解决方案进行全面分析。
此外,请确保您依赖于安全性的所有提供商功能都记录在您的提供商的 API 中,否则它们可能会在没有事先通知的情况下被删除,您最终会得到一个非常破碎的产品。
就我而言:
- 我很幸运,我的供应商在他们身边实施了所有这些对策。
- 在应用程序的初始测试期之后,我不依赖此进行身份验证(这不是我的应用程序的必需功能,只是一个方便的占位符预启动)
此外,在整个实现过程中,我对 oauth2 的了解已经足够多,因此非常值得。
如果您想了解更多信息,请阅读 RFC6819 和 RFC6749。我还发现这个网站非常有用。