OAuth2授权认证原理

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

OAuth2是OAuth协议的第2版,预计OAuth2.0将简化先前版中部分协议,促进不同应用间的互动性。

规范仍在起草中协议也在不断发展,但并不妨碍互联网巨头们对它的青睐。

OAuth2基础

角色

OAuth2有4种角色:资源所有者:

一般是自己/用户。

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

客户端:发起访问请求的应用:网站、javascript程序、移动app。

授权服务器:

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

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

OAuth2 令牌

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。

如:

资源所有者:you

资源服务器: a Facebook server

客户端:一个使用AngularJS的网站

授权服务器: a Facebook server

场景:

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

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

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

回调示例:

https://putdns.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 隐式授权)

密码凭证

什么时候使用密码凭证?

使用这种类型的授权,凭证先被发送给客户端,然后再发送到授权服务器。

这两个实体间必须有绝对的信任。客户端的开发来自于授权服务器同等权威的机构。

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

示例:

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

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

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

授权服务器:google服务器

场景:

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

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

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

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

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

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

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

密码授权时序图

oauth2

客户凭证授权

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

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

例子:

资源所有者:any website

资源服务器:Google Cloud Storage

客户端:the resource owner

授权服务器:a Google server

场景:

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

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

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

此处,终端用户不必拿到访问资源服务器的授权

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

客户凭证授权时序图

oauth2

Access token

Access token可使用多种方式发送到资源服务器,如:使用GET:

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

这种方式并不理想,令牌能在web服务器访问日志中找到。 

http请求头部携带授权参数

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

这种方式比较优雅,但并非所有资源服务器都允许这样做。 

OAuth2安全

OAuth2有时因其渗透性而受到批评,但往往是由于对协议的不良实现。

使用时应避免发出此类重大错误,以下列出一些参考。 

授权漏洞

此流程中存在一个漏洞,允许攻击者在特定条件下窃取用户帐号。

这个漏洞经常会出现在很多知名的网站中,这些网站并没有很好地实现这个流程。

示例:

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

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

单击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