NEWS.ALL
Ruizhao's News Reader

挖洞经验 | OAuth实现机制中的常见安全问题

FreeBuf /2020-05-21

1587462570_5e9ec1aa19edf.jpg

在测试网站安全性的过程中,我们会不时在某些服务端身上遇到OAuth授权认证机制,就比如登录Facebook的“Sign in with Google”按钮(以谷歌账户登录)一样,这就是典型的OAuth。本文就从安全角度简要讨论OAuth(特别是OAuth 2.0)在实现过程中存在的一些常见问题,顺带分享一些我在挖洞过程中遇到的场景。

快速了解

在讨论OAuth时,存在几个不同的版本和授权类型。如果要要阅读研究,建议你通读https://oauth.net/2/从中获得一些基本的知识了解。本文中,我就着重介绍最常见的OAuth 2.0授权码类型模式(authorization code),它也是授权流程中功能最完整、流程最严密的授权模式。本质上来说,OAuth为开发人员提供了一种授权机制,针对当前用户身份,允许应用程序从另一个应用程序(认证服务器)访问用户数据或对用户帐户执行某些操作。

比如,网站https://yourtweetreader.com可以为推特用户提供所有发贴(包括私密发贴)的显示功能,为了实现该功能,https://yourtweetreader.com需要部署OAuth 2.0机制,它会要求授权访问用户推特Twitter的权限以读取其中的发贴内容,在此之前,https://yourtweetreader.com会跳出一个授权页面,明确其向用户Twitter账户请求的具体内容。如果用户点击同意,https://yourtweetreader.com就会以用户身份访问用户的Twitter账户获取发贴内容,此时,https://yourtweetreader.com获得的权限是非常高的。

其中有几个关键的要素内容需要明白:

资源所有者(resource owner),也就是拥有应用程序账号的”用户”(user);

资源服务器(resource server),即服务提供商存放用户生成资源的服务器,或叫应用程序服务器,在此例中即是https://twitter.com。它与下面将要提到的认证服务器(authorization server),可以是同一台服务器,也可以是不同的服务器;

第三方应用程序(client application):又称”客户端”(client)应用,即此例中的https://yourtweetreader.com,它发起了希望获得用户Twitter账户权限的授权请求;

认证服务器(authorization server):认证服务器,即服务提供商专门用来处理认证的服务器,用户同意第三方应用程序发起的授权请求之后,该服务器会为第三方应用程序生成一个访问token,即此例中的https://twitter.com

客户端ID(client_id):第三方应用程序为用户生成的客户端ID号,通常来说它是一个公开的唯一标识符,第三方应用程序通过client_id来鉴别用户身份;

第三方应用程序密钥(client_secret):这是第三方应用程序发起授权请求时,认证服务器(authorization server)专门为第三方应用程序生成的一个密钥,它会连同其它认证参数经POST方式发送给认证服务器;

授权类型(response_type):表示授权类型,此例中为”code”授权码类型;

权限范围(scope):表示第三方应用程序向认证服务器申请的权限范围;

重定向URI(redirect_uri):表示重定向URI,即授权成功后要跳转到的地址;

客户端的当前状态(state):客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。由于它其中可是包含随机字符或唯一值,所以它的一个重要用处是可以用来当成请求中的CSRF防御手段;

授权模式(grant_type):表示使用的授权模式,此例中为”authorization_code”;

授权码(code):用户点击授权同意之后,由认证服务器(authorization server)返回给第三方应用的一个授权码,之后,第三方应用利用该code结合用户的client_id和client_secret向认证服务器(authorization server)换取访问令牌access_token;

访问令牌(access_token):第三方应用程序应用该令牌信息代表资源所有者的用户向资源服务器(resource server)发起请求访问;

更新令牌(refresh_token):第三方应用程序在后台用来获取下一次的访问令牌。

有了以上这些入门了解之后,以上述的显示推特发贴为例,来看看简单的OAuth流程:

1、用户访问https://yourtweetreader.com,点击“Integrate with Twitter”按钮;

2、https://yourtweetreader.comhttps://twitter.com发起授权请求,希望读取用户在Twitter资源服务器(resource server)上的推特发贴;用户点击“Integrate with Twitter”按钮后发生的请求大致如下:

https://twitter.com/auth

 ?response_type=code

 &client_id=yourtweetreader_clientId

 &redirect_uri=https%3A%2F%2Fyourtweetreader.com%2Fcallback

 &scope=readTweets

 &state=kasodk9d1jd992k9klaskdh123

3、之后会跳出一个授权同意页面:

4、点击授权同意后,链接会跳转到上步请求中的redirect_uri部份,并会加上Twitter分配给的code和state参数:

https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1&state=kasodk9d1jd992k9klaskdh123

5、https://yourtweetreader.com利用该code,结合client_id和client_secret,向Twitter资源服务器发起POST请求,以获得代表用户身份的访问令牌access_token。POST请求大致如下:

POST /oauth/access_token

Host: twitter.com

...

{"client_id": "yourtweetreader_clientId", "client_secret": "yourtweetreader_clientSecret", "code": "asd91j3jd91j92j1j9d1", "grant_type": "authorization_code"}

6、最终,上述流程完成之后,https://yourtweetreader.com就会利用获得的访问令牌access_token去读取用户在Twitter上的发贴内容了。

漏洞众测过程中会发现的OAuth问题

这里就分享几类我在漏洞众测过程中发现的OAuth问题,这些问题将或多或少对OAuth的实现形成安全影响:

重定向url的错误配置

这是OAuth实现中的一个常见安全问题,请求中的重定向url(redirect_uri)非常重要,认证服务器(authorization server)返回给第三方应用的授权码就是附加在该redirect_uri后的,如果该重定向url(redirect_uri)可被配置为其它攻击者控制的链接,那么也就说明攻击者可以获取授权码从而间接劫持用户账户。

该问题可能会存在于多种认证服务器(authorization server)中,有些认证服务器只会接受隶属第三方应用的url链接,而一些则会接受第三方应用的子域名链接,或是任何域名链接。

按照认证服务器(authorization server)部署的各种重定向url(redirect_uri)规则来看,这里存在几种redirect_uri规则绕过技巧,假设攻击者控制的网站为https://evil.com,且redirect_uri存在以下用户授权请求中:

https://api.twitter.com/oauth2/authorize?client_id=123050457758183&redirect_uri=https://yourtweetreader.com/callback

则可以有以下几种redirect_uri规则绕过技巧:

1、开放重定向: https://yourtweetreader.com/callback?redirectUrl=https://evil.com

2、路径遍历式绕过:https://yourtweetreader.com/callback/../redirect?url=https://evil.com

3、在redirect_uri中构造与第三方应用链接相似的域名链接:https://yourtweetreader.com.evil.com

4、通过构造请求链接,用referer header实现HTML注入和token窃取:

https://yourtweetreader.com/callback/home/attackerimg.jpg

state参数的错误处理

这也是OAuth实现中的一个常见安全问题,通常,state参数会被完全忽略或错误应用。如果请求中不存在state参数,或是其静态值总是不变,那么该OAuth流程可能会有跨站请求伪造问题(CSRF)。有时候,即使存在state参数,如果第三方应用不去验证它,那么也可能会被攻击者进行利用。假设用户点击第三方应用的授权请求之后,资源服务器就会分配给其一个授权码code,之后该授权码会连同state参数在第三方应用中执行一个跳转,如:

https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1&state=kasodk9d1jd992k9klaskdh123

我曾遇过state参数位置可被构造成另外一个URL来跳转利用,在redirect_uri形成第一次跳转后,state参数被攻击者形成了二次跳转。而且这种情况会在一些授权登录场景中出现,可能会导致账户劫持。我遇过的例子有:

攻击者可利用Slack的集成授权功能添加账户,间接窃取收信人的信息和相关消息提示;

攻击者可利用Stripe的集成授权功能重写覆盖支付记录,并窃取受害者账户的支付记录;

攻击者可利用PayPal的集成授权功能,向受害者账户中添加其它绑定账户,以此窃取受害者账户资金。

用邮箱地址进行“先前账户劫持”

另一种OAuth安全问题是,一些第三方应用不仅允许使用“Sign in with X”之类的授权登录,还允许用户名密码登录,这里就存在两种可攻击利用情况:

1、如果第三方应用在用户账户创建(creation)时缺乏邮件验证,那么攻击者可以在知晓受害者电邮地址的情况下,在受害者创建该应用账户之前,用受害者电邮地址加任意密码以受害者身份创建该应用账户。之后,一旦受害者在第三方应用中尝试创建或登录时,由于其电邮地址已被攻击者先前创建过,因此就会把受害者的创建或登录流程链接绑定到攻击者之前创建的账户中,这就是一种典型的“pre account takeover”(先前账户劫持)攻击,即攻击者利用受害者信息在受害者创建账户之前进行创建,实现账户劫持。

2、如果第三方应用在用户账户注册(sign up)时缺乏邮件验证,同样可以利用上述“pre account takeover”方法实现账户劫持。

认证流参数信息泄露

对于OAuth的各种认证流参数,严格来说,应该区分出哪些是保密且应受到保护的,就比如用户的client_id和client_secret遭到泄露的话,就非常危险,尤其是client_secret,攻击者可以利用受信客户端的实体身份来窃取受害者的访问令牌access_token和其它隐私信息,甚至实现账户劫持。回到我们的上述例子,来看以下过程:

https://yourtweetreader.com应用授权码、client_id 和 client_secret向资源服务器发起获取access_token的请求后,利用获得的access_token将会获得用户在资源服务器上的账户权限。

如果在客户端操作过程中client_secret 遭到泄露,那么攻击者就可能用其来获取代表受害者用户身份的access_token,再配合一些社会工程学技巧,攻击者还可以向OAuth授权添加更多范围。并且,由于请求来自受信客户端应用,因此它们都将显示为合法。

总结

在一些应用的OAuth实现过程中还有很多可被攻击利用的地方,这些问题非常普遍且令人惊讶。很多开发人员并不真正了解OAuth的安全部署,而OAuth一旦出现安全漏洞,将会导致非常严重的问题,如数据泄漏或账户劫持等。

*参考来源:medium,clouds 编译整理,转载请注明来自 FreeBuf.COM