OAuth2.0认证原理

OAuth2协议代表资源所有者,授予第三方应用程序,对资源服务器拥有有限的访问权限。

OAuth2.0是OAuth协议的第2版,预计OAuth2.0将简化先前版中的部分协议,促进不同应用程序间的互动性。规范仍在起草中协议也在不断发展。但不妨碍互联网巨头们对OAuth2的青睐。

OAuth2基础知识

角色(Roles)

OAuth2有4种角色:

资源所有者:一般是自己/用户。

资源服务器:存放数据的服务器。

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

授权服务器:授权服务器给客户端发放访问令牌(access token),客户端使用令牌访问资源服务器中的数据,资源服务器与授权服务器可以相同(同一个物理服务器、同一个应用程序)。

令牌(Tokens):令牌是授权服务器生成的随机字符串。

OAuth2 Tokens

OAuth2有两种token,access token和refresh token。

访问令牌/Access Token

Access Token很重要是因为它允许第三方应用访问用户数据。Access Token以参数的形式发送到资源服务器。Access Token的生命周期有限,由授权服务器定义。

注:Access Token须保密,若发送请求的客户端是web浏览器,Access Token的安全问题就更棘手了。

刷新令牌/Refresh Token

Refresh Token与Access Token一同被发出,与Access Token不同是,Refresh Token不是每次请求资源服务器时都带着。仅在Access Token过期时,向授权服务器更新Access Token时用。

注:出于安全考虑,并不总是能拿到这种令牌,稍后会看到这种情况。

令牌作用域

Token的作用域是一个参数,用于限制Access token的权限,当客户端申请Access token时,须发送想要获得的作用域,范围越小授权通过的可能性就越大,可用的作用域列表,由授权服务器定义。

注:详情参见Protocol Endpoints

OAuth2 HTTPS

OAuth2要求客户端与授权服务器间的通信使用https,敏感数据在两者之间传送。

注:实际上,如果是自己的授权服务器,未必一定要采用https,但须清楚不采用https所带来的安全问题。 

注册客户端

使用OAuth2从资源服务器检索数据,需注册成为授权服务器的客户端。

注:对于这一点提供者可以自行选择,协议中只规定了客户端须传的参数,和授权服务器应返回的数据。

客户端参数

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

redirect_uri:客户端接收授权代码和access token的地址。

grant_type:客户端所能使用的授权类型。

Javascript源:允许ajax访问资源服务器时所用的主机名(hostname),此参数为可选项。 

授权服务器返回数据

Client Id:随机字符串,具有唯一性的。

Client Secret:密钥,须保密。

授权类型

OAuth2定义了4种授权类型,根据客户端获取access token时的位置和性质决定。以下将介绍四种类型的授权方式。

授权码

什么时候使用授权码?

只要客户端是web服务器,就应使用它。获得一个长期的access token,使用Refresh Token进行更新。

注:如果授权服务器启用这种方式。

如:

Resource Owner/资源所有者:you

Resource Server/资源服务器:a Google server

Client:any website

Authorization Server/授权服务器:a Google server

场景:

1.一个网站想要获取你在谷歌的个人资料。

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

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

4.客户端使用授权码,向授权服务器获取access token。

5.网站使用access token,查询资源服务器(Resource Server),获取个人资料。

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

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

授权码获取时序图

Authorization Code Grant

隐式授权

什么时候使用隐式授权?

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

如:

Resource Owner/资源所有者:you

Resource Server/资源服务器: a Facebook server

Client/客户端:如,一个使用AngularJS的网站

Authorization Server/授权服务器: a Facebook server

场景:

1.客户端想要获取用户在Facebook的资料。

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

3.授权通过,授权服务器将用户重定向到网站,如下的URI片段中包含access token。

回调示例:

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

4.客户端解析出access token,使用access token访问资源服务器(Facebook)。

查询示例:

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

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

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

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

隐式授权时序图

oauth2 Implicit Grant(oauth2 隐式授权)

密码凭证

什么时候使用密码凭证?

使用这种类型的授权,凭证先被发送给客户端,然后再发送到授权服务器。这两个实体间必须有绝对的信任。主要用于客户端的开发来自于授权服务器同等权威的机构。

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

示例:

Resource Owner/资源所有者:在google.com网站上有一个帐户

Resource Server/资源服务器:google公司在api.google.com上公开API

Client/客户端:google.com网站,来自google公司

Authorization Server/授权服务器:google服务器

场景

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

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

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

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

5.服务器端应用程序与授权服务器交换用户凭证,获取access token。

6.应用程序使用access token访问资源服务器(api.acme.com)。

密码凭据授权更多信息参见RFC 6749

密码授权时序图

oauth2

客户凭证授权

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

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

例子:

Resource Owne/资源所有者:any website

Resource Server/资源服务器:Google Cloud Storage

Client/客户端:the resource owner

Authorization Server/授权服务器:a Google server

场景:

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

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

身份验证通过后,网站获得一个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有时因其渗透性而受到批评,但往往是由于对协议的不良实现。使用时应避免发现这类重大错误,以下是一些参考。 

授权漏洞

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

示例:

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

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

单击A网站的Facebook Connect按钮,拿到重定向的url,包含授权代码。回调类似如下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。 

隐性授权中的漏洞

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

例子:

攻击者想窃取用户在A网站上的账户,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

点击劫持

这种技术允许攻击者,将授权页面隐藏在透明的iframe中,让用户者点击授权页面的“允许”按钮以触发链接完成作弊。

示例:

oauth2 clickjacking

点击劫持解决办法

为了避免这类问题,授权服务器须在授权页面返回一个名为X-Frame-Options的头文件,其值为DENY或SAMEORIGIN。以此防止授权页面显示在iframe 中,或者要求主页的域名与iframe“src”属性中的域名保持一致。

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

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

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

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