OAuth2.0认证原理全面解读

如果OAuth2对你来说仍然是一个模糊的概念,或者只为确定是否真正理解了OAuth2,那么本文应该能够给你答案。

你猜对了,OAuth2是OAuth协议的第2版(也称为框架)。

OAuth2协议代表资源所有者,授予第三方应用程序,对HTTP服务有限的访问权限;或允许第三方应用程序代表自己获取访问权限。发起请求的客户端,可以是网站或移动应用程序。

预计OAuth2将简化先前版中的一些协议,并促进不同应用程序间的互动性。规范仍在起草中,协议也在不断发展,但这都不妨碍互联网巨头们(Google、Facebook)对OAuth2的青睐。

OAuth2基础知识

角色(Roles)

OAuth2定义了4种角色:

资源所有者:一般是你自己

资源服务器:托管数据的服务器(例如在Google托管个人资料和信息)。

客户端:发起访问请求的应用程序(可以是网站,Javascript程序或移动app)。

授权服务器:此服务器向客户端发出访问令牌(access token),令牌用于客户端向资源服务器请求资源,资源服务器可以与授权服务器相同(相同的物理服务器、相同的应用程序)。

令牌(Tokens):令牌为授权服务器生成的随机字符串,在客户端发起令牌请求时发出。

OAuth2两种令牌(Tokens)

Access Token(访问令牌):

Access Token最为重要,因为它允许第三方应用程序访问用户数据。Access Token能够以参数的形式(或直接放在请求头中)发送到资源服务器。Access Token的生命周期有限,由授权服务器定义。Access Token必须保密,但这并不容易,如果发送请求的客户端是web浏览器,由Javascript向资源服务器发送请求,Access Token安全就更棘手了。

Refresh Token(刷新令牌)

Refresh Token与Access Token一起发出,但是与Access Token有所不同,Refresh Token用每次请求资源服务器时都带着。它仅在Access Token过期时,向授权服务器更新Access Token时用。出于安全原因,并不总是能获得这种令牌,稍后会看到这种情况。 

Access token scope(令牌作用域)

令牌作用域是一个参数,用来限制Access token的权限,当客户端申请Access token时必须发送想要的作用域,范围越小,授权通过的可能性就越大,可用的作用域列表由授权服务器定义。 更多详情参见Protocol Endpoints

OAuth2中的HTTPS

OAuth2要求客户端与授权服务器之间的通信使用HTTPS,因为,敏感数据在两者之间传送(令牌和资源所有者凭证)。实际上,如果是自己的授权服务器,就未必一定要采用HTTPS,但必须清楚不采用HTTPS所带来的安全问题。 

Register as a client(注册客户端)

如果使用OAuth2从资源服务器检索数据,需要注册成为授权服务器的客户端,对于这一点上提供者可以自行选择,协议只规定了客户端必须传的参数,和授权服务器要返回的数据。

OAuth2客户端参数

应用程序名称:应用程序名称。

重定向url:客户端接收授权代码和access token的地址。

授予类型:客户端请求所能使用的授权类型。

Javascript起源(可选):允许通过XMLHttpRequest(ajax)请求资源服务器时所用的主机名(hostname)。 

OAuth2授权服务器响应数据(Authorization server response)

Client Id(客户端ID):具有唯一性的随机字符串。

Client Secret(客户端密钥):密钥必须保密。

OAuth2 Authorization grant types(授权类型)

OAuth2定义了4种授权类型,会根据客户端获取access token时的位置和性质来决定。

Authorization Code Grant(授权码)

什么时候使用授权码?

只要客户端是web服务器,就应该使用它。可以获得一个长期的access token,使用Refresh Token进行更新(如果授权服务器启用这种方式)。

例如:

Resource Owner(资源所有者):you/个人信息

Resource Server(资源服务器):a Google server

Client(客户端): any website

Authorization Server(授权服务器):a Google server 

场景:

1、一个网站(any website)想要获得关于你在谷歌的个人资料和信息。

2、客户端将你重定向到授权服务器(Authorization server)。

3、如果授权通过,授权服务器(Authorization server)会在回调响应中,向客户端(any website)发送授权码。

4、然后,客户端以此授权码,与授权服务器交换获得access token。

5、网站(any website)现在可以使用这个access token,查询资源服务器(Resource Server),并检索你的个人概要信息。

用户永远不会看到access token,它由网站进行存储(例如存在session中),谷歌还可以通过access token发送其它信息,比如令牌的生命周期和Refresh Token。

上述是一种理想的场景,也是一个相对安全的场景,因为access token没有在客户端(示例中为web浏览器)传递。更多详情参见Authorization Code Grant。 

 

授权码获取时序图

Authorization Code Grant

Implicit Grant(隐式授权)

什么时候使用隐式授权?

隐式授权通常在客户端使用脚本语言(如Javascript)在浏览器中运行时,此授权类型不允许有refresh token。

例如:

Resource Owner(资源所有者):you/个人信息

Resource Server(资源服务器): a Facebook server

Client(客户端):例如,一个使用AngularJS的网站

Authorization Server(授权服务器): a Facebook server

场景:

1、客户端(AngularJS)想要获得关于用户在Facebook的资料和信息。

2、用户被浏览器重定向到授权服务器(Facebook)。

3、如果授权通过,授权服务器将用户重定向到网站,URI片段中包含access token(不是发送到web服务器)。

回调示例:

https://xieyonghui.com/oauthcallback#access_token=MzJmNDc3M2VjMmQzN

4、客户端(AngularJS)通过解析access token,然后用它去查询资源服务器(Facebook)。

查询示例:

https://graph.facebook.com/me?access_token=MzJmNDc3M2VjMmQzN

有关跨域资源共享(CORS)的更多信息参见Access_control_CORS

注意!仅当没有其他授权类型可用时,才应使用此类授权。实际上,它是最不安全的,因为access token暴露在客户端(易受攻击)。

隐式授权的更多信息参见RFC 6749

隐式授权时序图

oauth2 Implicit Grant(oauth2 隐式授权)

资源所有者密码凭据授予

(Resource Owner Password Credentials Grant)

什么时候密码凭据使用?

使用这种类型的授权,凭证(以及密码)将被发送到客户端,然后发送到授权服务器。因此,这两个实体之间必须有绝对的信任。它主要用于和授权服务器有同等权威的机构所开发的客户端。

例如,可以想象一个名为xieyonghui.com的网站,试图访问其子域api.xieyonghui.com中的受保护资源。用户只用在xieyonghui.com网站上输入用户名/密码,因为他的帐户是在该站点上创建的。

示例:

Resource Owner(资源所有者): 你在Acme公司的acme.com网站上有一个帐户

Resource Server(资源服务器): Acme公司在api.acme.com上公开其API

Client(客户端): acme.com网站,来自Acme公司

Authorization Server(授权服务器): Acme服务器

场景

1、Acme公司认为可以向第三方应用程序提供RESTful API。

2、这家公司认为使用自己的API是很方便并且避免重新发明轮子。

3、公司需要一个访问令牌来调用自己API的方法。

4、为此,公司要求用户通过标准方式填写登录信息,就像正常登录一样。

5、服务器端应用程序(acme.com)与授权服务器的交换用户的凭证,并获取access token(如果用户凭证有效)。

6、这时应用程序就可以使用access token查询资源服务器(api.acme.com)。

资源所有者密码凭据授权更多信息参见RFC 6749

密码授权时序图

oauth2

客户凭证授权(Client Credentials Grant)

什么时候使用客户凭证授权?

当客户端本身就是资源所有者时,将使用这种类型的授权。不用从终端用户那里获得授权。

例子:

Resource Owne(资源所有者):any website

Resource Server(资源服务器):Google Cloud Storage

Client(客户端): the resource owner

Authorization Server(授权服务器): a Google server

场景:

一个网站在谷歌云中存储了文件。

网站必须通过谷歌API检索或修改文件,并且必须通过授权服务器进行身份验证。

经过身份验证后,网站将获得一个access token,该access token现在可用于查询资源服务器(谷歌云存储)。

在这里,终端用户不必拿到访问资源服务器的授权。

客户端凭据授权更多信息参见RFC 6749

客户凭证授权时序图

oauth2

Access token的用法

Access token可以通过多种方式发送到资源服务器,使用GET的示例:

https://api.xieyonghui.com/profile?access_token=MzJmNDc3M2VjMmQzN

这种方式并不理想,因为令牌可以在Web服务器的访问日志中找到。 

http请求头部带授权参数

GET / profile HTTP / 1.1 
Host:api.xieyonghui.com
Authorization:Bearer MzJmNDc3M2VjMmQzN

这种试比较优雅,但并不是所有的资源服务器都允许这样做。 

OAuth2安全

OAuth2有时因其渗透性而受到批评,但往往都是由于对协议的不良实现。使用它时要避免发现重大错误,这里有一些例子可以参考。 

授权漏洞(Vulnerability in Authorization Code Grant)

此流程中存在一个漏洞,允许攻击者在特定条件下窃取用户的帐户。这个漏洞经常会出现在很多知名的网站(如Pinterest、SoundCloud、Digg等等)中,因为这些网站并没有很好地实现这个流程。

示例:

受害者在一个叫A的网站上有一个账号。

A网站允许用户使用Facebook登录或注册,因为A网站已在Facebook OAuth2授权服务器上注册成为客户端。

单击网站A的Facebook Connect按钮,获得了重定向的url(包含授权代码)

(回调类似于http://site-internet-a.com/facebook/login?code=OGI2NmY2NjYxN2Y4YzE3)

可以在Firefox安装了NoRedirect插件,或使用Burp看到这个链接。现在如果强迫受害者访问这个url(通过在网站中隐藏的iframe,或在电子邮件中的一张图片)。

如果受害者登录了A网站,那就中招了!现在可以用其他人的Facebook账号,访问受害者在A网站的账号。只需要点击Facebook连接按钮,就可以连接到受害者的账户。

授权漏洞的解决方法

有一种方法是通过添加“state”参数来防止这种情况。这只是建议,OAuth2规范中并没有这样的要求。如果客户端在请求授权代码时发送此参数,则响应中的授权服务器将返回该参数,并且在通过授权代码交换access token之前,客户端对“state”参数进行比较验证。此参数是一个具有唯一性的随机数的哈希值,存储在用户session中。

在示例中,如果A网站使用“state”参数,他会发现回调中的哈希值,与受害者会话中存储的哈希值不匹配,因此可以防止受害者帐户被盗。

跨站请求伪造更多信息参见RFC 6749。 

隐性授权中的漏洞(Vulnerability in Implicit Grant)

这种类型的授权是最不安全的,因为它将access token暴露给客户端(大多数时候是Javascript)。有一个常见的漏洞,源于客户端不知道access token是否来自真正的服务器(混淆代理问题),这存在攻击者窃取用户帐户的情况。

例子:

攻击者为窃取受害者在A网站上的账户,该网站允许通过Facebook账户连接,并使用隐式授权。

攻击者创建了一个允许通过Facebook登录的B网站。

受害者使用他的Facebook帐户登录到网站B,因此,隐式授权为用户生成了B网站的access token。

攻击者通过B网站获得了access token,以此来修改在网站A上的URI片段中的access token,如果A网站对此类攻击的未做防范,受害者的帐户就会受到攻击,攻击者现在可以访问受害者在网站A上的账户了。

隐性授权漏洞的解决方法

为避免这种情况,授权服务器必须在其API中提供检索access token信息的方法。因此,网站A能够将攻击者的access token对应的client_id,与自己网站用户的client_id进行比较。由于盗用网站B生成的access token,它的client_id将与网站A的client_id不同,因此,就可以拒绝连接。

Google在其API文档中对此类问题进行了描述,更多关于的信息参见RFC

点击劫持(Clickjacking)

这种技术允许攻击者,通过将授权页面隐藏在透明的iframe中,并让受害者点击在授权页面的“允许”按钮以触发链接来作弊。

示例

oauth2 clickjacking

点击劫持解决办法

为了避免这种情况,授权服务器必须在授权页面上返回一个名为X-Frame-Options的头文件,其值为DENY或SAMEORIGIN。这可以防止授权页面显示在iframe (DENY)中,或者要求主页的域名与iframe“src”属性(SAMEORIGIN)中指定的域名保持一致。

X-Frame-Options不是一个标准的header,但是以下这些版本的浏览器都支持:

IE8+, Firefox3.6.9+, Opera10.5+, Safari4+, Chrome 4.1.249.1042+

更多关于这方面的信息参见X-Frame-Options

关于OAuth2协议实施中的潜在漏洞和对策参见RFC