OAuth 2.0 / RCF6749 协议解读
2017-09-23

OAuth是第三方应用授权(Authorization)的开放标准,目前最新版本是2.0,以下将要介绍的内容和概念主要来源于该版本;OAuth中对于认证(Authentication)的实现是比较弱的(User Authentication with OAuth 2.0),因此实际应用中最好能整合其他协议或扩展,如使用OpenID Connect,来实现比较强的认证。恐篇幅太长,OAuth 的诞生背景就不在这里赘述了,可参考 RFC 6749 。

  四种角色定义:

  1. Resource Owner:资源所有者,即终端用户
  2. Resource server:资源服务器,即提供资源存储访问一方
  3. Client:通常指第三方应用
  4. Authorization server:授权服务器

  协议端点(URI):OAuth给授权过程定义了Authorization、Token和Redirection三种端点。Authorization端点用来完成用户授权,在授权码模式(Authorization Code)和隐含模式(Implicit)下被用到;Token端点用来交换与获取access token,不能包含fragment(hash),在隐含模式(Implicit)下则无需提供该端点;Redirection端点用来接收授权凭证,Public客户端或者Implicit授权的Confidential客户端必须注册其Redirection端点。

  客户端类型:OAuth根据是否能够进行安全认证定义了两种客户端类型:机密型客户端(Confidential)和公开型客户端(Public)。其中机密型客户端有Web应用,公开型客户端包括User Agent Based和Native应用。客户端的类型注册时确定,不能由授权服务器假定。如果一套应用包含多个不同类型的客户端,这些不同部分应分开单独注册。

  客户端认证(Client Authentication):客户端认证当满足授权服务器的安全要求,对机密型客户端的认证可依赖授权服务器发布的认证凭证(比如Password, Public/Private密钥对),而要对公共客户端进行认证很可能是不可靠的。客户端在每次请求中只能使用一种认证方法,如果客户端持有Password,可采用HTTP Basic认证或request-body传递身份凭证参数方法。客户端认证带来的益处:

  1. 强制绑定Refresh Token或Authorization Code到客户端
  2. 通过禁用或修改其凭证来快速恢复沦陷客户端
  3. 定期凭证轮换,更容易实现认证管理

  访问令牌(Access Token)是什么?Access Token是访问被保护资源的凭证,一个用来表明被授予权限的字符串,可以是一种可取回授权信息的标识,也可以自包含授权信息于内。可参考 RFC6750 - OAuth 2.0 Bearer Token Usage 。

  更新令牌(Refresh Token)是什么?当Access Token无效或过期后,客户端将使用Refresh Token来请求授权服务器更新Access Token,除此而外别无他用。通常Refresh Token是授权服务器在发布新Access Token的同时可选发布的,与客户端绑定并长期有效,但是只有授权码模式(Authorization Code)和用户密码模式(Resource Owner Password Credentials)支持Refresh Token。授权服务器须执行如下操作:

  1. 对于认证型客户端,要求进行认证
  2. 如果请求中包含客户端认证,则执行认证流程,还要保证Refresh Token是发布给认证客户端的
  3. 验证Refresh Token是否有效

  Transport Layer Security (TLS):授权服务器和资源服务器都必须实现TLS,至于客户端最好也实现TLS。如果客户端没有实现TLS,授权服务器在发出重定向之前应向用户发出安全告警信息。

  OAuth授权的基本流程如下:

  

  1. 用户打开客户端以后,客户端要求用户给予授权
  2. 用户同意给予客户端授权
  3. 客户端使用上一步获得的授权,向授权服务器申请令牌
  4. 授权服务器对客户端进行授权以后,确认无误,同意发放令牌
  5. 客户端使用令牌,向资源服务器申请获取资源
  6. 资源服务器确认令牌无误,同意向客户端开放资源

  OAuth针对不同场景详细定义了四种授权模式:授权码模式(Authorization Code)、隐含模式(Implicit)、用户密码模式(Resource Owner Password Credentials)和客户端证书模式(Client Credentials)。另外,你也可以使用其他扩展模式。

 

一. 授权码模式(Authorization Code)

  授权码模式是流程最严密的授权模式,但是如果被用于Public客户端授权,由于该客户端不能持有客户端证书,因此无法进行身份认证。

  • 流程解析: 
    1. 用户访问客户端,后者将前者导向授权服务器
    2. 用户选择是否给予客户端授权
    3. 假设用户给予授权,授权服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码
    4. 客户端收到授权码,附上早先的"重定向URI",向授权服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见
    5. 授权服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)或者更新令牌(refresh token)

  该流程中客户端先获取授权码再交换访问令牌,似乎获取授权码显得多余?其实授权码除了代表授权范围之外,还避免了暴露访问令牌于用户端:

The authorization code provides a few important security benefits,such as the ability to authenticate the client, as well as the transmission of the access token directly to the client without passing it through the resource owner's user-agent and potentially exposing it to others, including the resource owner

 

二. 隐含模式(Implicit)

  授权码模式的简化版,跳过了"授权码"这一步,可适用于在浏览器中实现的应用,访问令牌暴露于用户端;在该模式下,授权服务器不会认证客户端,不能使用Refresh Token,一旦Access Token过期,需要重新进行授权处理;隐含模式是一个基于redirection的流,在某些情况下,客户端身份是可以通过redirection URI来验证的。在授权码模式可用的情况下,应权衡隐含模式的便利性和安全性。

  • 流程解析:

    1. 客户端将用户导向认证服务器
    2. 用户决定是否给于客户端授权
    3. 假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌
    4. 浏览器向Web-Hosted服务器发出请求,其中不包括上一步收到的Hash值
    5. Web-Hosted服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌
    6. 浏览器执行上一步获得的脚本,提取出令牌
    7. 浏览器将令牌发给客户端

  • 流程参数解释:此处不再详述,可参考理解OAuth 2.0 或 RFC6749

 

三. 用户密码模式(Resource Owner Password Credentials)

  用户向客户端提供自己的用户名和密码,客户端使用这些信息向授权服务器索要授权,但不得保存用户的密码。须在用户对客户端高度信任(比如客户端是操作系统的一部分,或者由一个著名公司出品,或者客户端是本公司出品),而认证服务器无法在其他授权模式下完成授权的情况下,才能考虑使用这种模式。

  • 流程解析:
    1. 用户向客户端提供用户名和密码
    2. 客户端将用户名和密码发给认证服务器,向后者请求令牌
    3. 认证服务器确认无误后,向客户端提供访问令牌

 

四. 客户端证书模式(Client Credentials)

  顾名思义,客户端模式不是针对单个用户的授权,而是针对客户端授权,适用于受保护资源已经处于客户端控制之下(相当于资源所有者)或者授权服务器已经预先配置好客户端访问权限的的情况。

  • 流程解析:
    1. 客户端向认证服务器进行身份认证,并要求一个访问令牌
    2. 认证服务器确认无误后,向客户端提供访问令牌

  • 流程参数解释:此处不再详述,可参考理解OAuth 2.0 或 RFC6749

 

五. 授权模式选择

  • 本公司出品应用一般适用用户密码模式(Resource Owner Password Credentials)
  • Web应用一般适用授权码模式(Authorization Code)
  • Native应用一般适用授权码模式(Authorization Code)或隐含模式(Implicit):Native应用指安装、运行在用户设备上的客户端,包括桌面应用、手机客户端等
    When choosing between the implicit grant type and the authorization code grant type, the following should be considered:
    
    . Native applications that use the authorization code grant type SHOULD do so without using client credentials, due to the native application's inability to keep client credentials confidential.
    . When using the implicit grant type flow, a refresh token is not returned, which requires repeating the authorization process once  the access token expires.
  • User Agent Based应用一般适用隐含模式(Implicit)

  • 机器对机器一般适用客户端证书模式(Client Credentials) 

 

六. 安全风险

  • 客户端认证(Client Authentication):对Public客户端进行认证很可能是不可靠的,授权服务器可以尝试使用redirection URI或征募用户来验证客户端身份

       The authorization server MUST NOT issue client passwords or other
       client credentials to native application or user-agent-based
       application clients for the purpose of client authentication.  The
       authorization server MAY issue a client password or other credentials
       for a specific installation of a native application client on a
       specific device.
       ......
       A valid
       redirection URI is not sufficient to verify the client's identity
       when asking for resource owner authorization but can be used to
       prevent delivering credentials to a counterfeit client after
       obtaining resource owner authorization.

  • 更新令牌(Refresh Tokens):授权服务器可以给Web客户端和Native客户端发布Refresh Token。Refresh Token在整个生命周期中都应该保持机密性,不能泄露给任何无关的第三方;Refresh Token必须与客户端身份相绑定;Refresh Token不能未获授权而被生成、修改、猜测产生


      the authorization server could employ refresh token
       rotation in which a new refresh token is issued with every access
       token refresh response.  The previous refresh token is invalidated
       but retained by the authorization server.  If a refresh token is
       compromised and subsequently used by both the attacker and the
       legitimate client, one of them will present an invalidated refresh
       token, which will inform the authorization server of the breach

  • 授权码(Authorization Codes):授权码有效期短,单一用途

       If the
       authorization server observes multiple attempts to exchange an
       authorization code for an access token, the authorization server
       SHOULD attempt to revoke all access tokens already granted based on
       the compromised authorization code.
  • 跨站请求伪造(Cross-Site Request Forgery):一同发送一个non-guessable state请求参数可用来防止CSRF

       A CSRF attack against the client's redirection URI allows an attacker
       to inject its own authorization code or access token, which can
       result in the client using an access token associated with the
       attacker's protected resources rather than the victim's (e.g., save
       the victim's bank account information to a protected resource
       controlled by the attacker).
    
       The client MUST implement CSRF protection for its redirection URI.
       This is typically accomplished by requiring any request sent to the
       redirection URI endpoint to include a value that binds the request to
       the user-agent's authenticated state (e.g., a hash of the session
       cookie used to authenticate the user-agent).  The client SHOULD
       utilize the "state" request parameter to deliver this value to the
       authorization server when making an authorization request.

  • 点击劫持(Clickjacking):

      To prevent this form of attack, native applications SHOULD use
       external browsers instead of embedding browsers within the
       application when requesting end-user authorization.  For most newer
       browsers, avoidance of iframes can be enforced by the authorization
       server using the (non-standard) "x-frame-options" header.  This
       header can have two values, "deny" and "sameorigin", which will block
       any framing, or framing by sites with a different origin,
       respectively.  For older browsers, JavaScript frame-busting
       techniques can be used but may not be effective in all browsers

 

七. 相关参考