技术咖 - 全面了解 OAuth 协议(一)

本文就 OAuth 协议如何从最初的 OAuth 1.0 版本一步步优化、迭代、重组后到现今的 OAuth 2.1 版本进行剖析。方便开发者在使用 OAuth时能更好地理解、适配更多场景,丰富业务功能。

OAuth,对于应用程序开发人员或相关软件开发行业人员可能非常熟悉,它与我们日常冲浪生活息息相关。如在美团中使用微信登录、在网易云中使用QQ登录等,这些平台彼此之间的交互及数据打通,就是 OAuth 实现的。在万物互联互通的时代,OAuth 已是应用对外交互和共享信息不可或缺的方式。

OAuth 是什么?

OAuth 不是一个 API 或者服务,而是一种开放的授权协议,用于允许 web、移动和桌面应用程序以简单和标准的方法进行安全授权。"An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications."——OAuth 官网介绍

注意,OAuth 关注的是 Authorization(授权)的层面(即“What”),而不是 Authentication(认证)的层面(即“Who”),关于Authentication(认证)相关的标准协议我们后面会有专门的文章描述。

OAuth能解决什么问题?

在传统的授权领域中,第三方应用请求用户的受保护的资源时,服务端会通过使用用户的账密凭证在服务器上进行身份验证(HTTP Basic Authentication),以便为第三方应用程序提供访问权限,这也就进一步要求用户与第三方应用之间共享授权凭证。

譬如,应用 A 想访问用户 B 的支付宝账号信息,按照传统的解决方案,那就是应用 A 要求用户 B 在应用 A 中输入支付宝账号密码,然后应用 A 再使用用户 B 的账号密码去支付宝获取该用户的账号信息。这种方式无疑会造成下面几种问题:

  1. 第三方应用需要提前存储用户的账号密码以供将来使用;
  2. 尽管使用密码存在安全缺陷,但服务器仍然需要支持密码身份验证;
  3. 第三方应用可以直接获取用户所有的受保护的资源,但用户却无法对具体的资源行使过于精细化的访问控制权;
  4. 用户如果想撤销第三方应用对某个资源的访问权限,只能更改账号密码;
  5. 极其容易造成由于第三方应用的安全问题导致用户数据的大批量泄露;

OAuth 的出现,彻底改变了双方系统之间只能通过暴露账密去授权获取用户数据的方式。OAuth 通过在第三方应用与服务提供方之间引入了一个授权层来解决这些问题。首先就要求第三方应用不能直接登录服务提供方,只能先登录授权层,获取到授权层颁发的访问令牌,然后通过这个令牌才能获取到相应权限的资源(并非全部资源),以此将第三方应用与服务提供方区分开。

接下来我们将从OAuth1.0、OAuth1.0A、OAuth2.0、OAuth2.1详细展开,为大家剖析OAuth协议。

关于OAuth1.0

OAuth 1.0 包含哪些角色

  • Service Provider:服务提供商,即允许使用 OAuth 协议访问的应用,可以理解为 OAuth 授权中的身份提供方;
  • User:指在 Service Provider 中拥有独立账户的个人;
  • Consumer:对应服务提供者的消费方,代表用户使用 OAuth 协议访问服务提供商的网站或应用程序;

如何理解这三个角色?我们以使用支付宝登录淘宝为例,举个简单的例子:

技术咖 | 全面了解 OAuth 协议(一)

图一:淘宝的登录页面

如上图,User 对应的是当前需要登录到淘宝的真实用户,Service Provider 对应的就是支付宝平台,Consumer 就是淘宝本身。

OAuth 1.0 的授权流程


技术咖 | 全面了解 OAuth 协议(一)

OAuth 1.0 标准的授权流程

如上图。还是以使用支付宝登录淘宝为例简单的使用文字描述以上流程:

  • 前置流程:用户进入淘宝登录页,点击使用“支付宝”登录;

A. 淘宝(Consumer)向支付宝(Service Provider)申请 Request Token(换取 Access Token 的临时令牌);

请求参数

  • oauth_consumer_key:淘宝在支付宝申请的 OAuth 应用的 key
  • oauth_signature_method:对请求进行签名的签名方法
  • oauth_signature:请求的签名
  • oauth_timestamp:当前发起请求的时间戳,精确到秒
  • oauth_nonce:一个随机字符串,必须保证唯一
  • oauth_version:必须传 1.0

B. 支付宝(Service Provider)签发未授权的 Request Token 后返回淘宝(Consumer),引导用户跳转到支付宝(Service Provider)对 Request Token 进行授权;

响应参数

  • oauth_token:请求令牌 Request Token
  • oauth_token_secret:请求令牌对应的密钥

C. 支付宝(Service Provider)获取用户的授权;

请求参数

  • oauth_token:Request Token
  • oauth_callback:授权成功后的重定向地址

D. 支付宝(Service Provider)向淘宝(Consumer)颁发 Request Token;

响应参数

  • oauth_token:用户授权过的 Request Token。

E. 淘宝(Consumer)使用 Request Token 换取 Access Token(访问令牌);

请求参数

  • oauth_consumer_key:淘宝在支付宝申请的 OAuth 应用的 key
  • oauth_token:上一步获取到的 Request Token
  • oauth_signature_method:对请求进行签名的签名方法
  • oauth_signature:请求的签名
  • oauth_timestamp:当前发起请求的时间戳,精确到秒
  • oauth_nonce:一个随机字符串,必须保证唯一
  • oauth_version:必须传 1.0

F. 支付宝(Service Provider)授权并向淘宝(Consumer)颁发 Access Token;

响应参数

  • oauth_token:用户的访问令牌
  • oauth_token_secret:访问令牌的密钥
  • G. 淘宝(Consumer)访问用户在支付宝(Service Provider)受保护的资源(个人信息等);

请求参数

  • oauth_consumer_key:淘宝在支付宝申请的 OAuth 应用的 key
  • oauth_token:上一步获取到的 Access Token
  • oauth_signature_method:对请求进行签名的签名方法
  • oauth_signature:请求的签名
  • oauth_timestamp:当前发起请求的时间戳,精确到秒
  • oauth_nonce:一个随机字符串,必须保证唯一
  • oauth_version:必须传 1.0

以上流程基本就是 Consumer 使用 Request Token 换取 Access Token 的过程。Request Token 和 Access Token 的使用范围和生命周期是不一致的。Request Token 只是获取 Access Token 的前置属性,无法通过该 token 直接获取用户的受保护资源,并且生命周期较短;而 Access Token 是访问用户受保护资源的唯一令牌,生命周期较长,OAuth 1.0 中一般可以以年为单位去设置该值的有效期。

关于OAuth 1.0 A

2009年6月24日,OAuth 1.0 A 发布,修订了 1.0 的严重的安全漏洞(Session Fixation Attack):OAuth Security Advisory: 2009.1

OAuth 1.0 A 修复了什么问题?

按照官方的解释:攻击者首先初始化一个合法的会话(oauth_callbak 配置成攻击者的地址),然后诱使正常用户授权该会话,用户授权成功后回调到攻击者的服务器,从而拿到正常用户的 Access Token,进而获取到正常用户的任何受保护的资源。

OAuth 1.0 A 的流程

技术咖 | 全面了解 OAuth 协议(一)

图三:OAuth 1.0 a 授权流程

如图中高亮标注的部分,OAuth 1.0 A做出的优化主要在于:

  1. Consumer 获取 Request Token 时,必须传递 oauth_callback 并用于签名;
  2. Service Provider 授予 Consumer Request Token 时,必须返回 oauth_callback_confirmed,主动告知 Consumer 是否在第一步传递了 oauth_callback;
  3. Consumer 引导用户授权 Request Token 时去掉了 oauth_callback 参数;
  4. Service Provider 向 Consumer 颁发 Request Token 时,新增了一个 oauth_verifier;
  5. Consumer 向 Service Provider 换取 Access Token 时,将上一步获取的 oauth_verifier 一并传给 Service Provider,由 Service Provider 校验 oauth_verifier 的合法性。

OAuth 1.0 A既然都已经修复了 1.0 遗留的问题,那么为什么还要重新制定一个 2.0 版本呢?其实这个问题,细看一下 OAuth 1.0 的授权流程就可以猜到一二,1.0 虽然经过修复后没有什么太过致命的安全问题,但是从整个使用流程来看 1.0 还是太过于复杂、繁琐,对于开发者来说极其不友好。另外,1.0 对于桌面端、移动端应用来说,无法很好地集成,因为 1.0 十分关键的一点就是 oauth_callback 的存在,而像桌面端、移动端此类应用通常没有服务端,所以在使用 1.0 的时候就要为了兼容这种设计上的错误而冒很大的风险(泄露 consumer_key 和 consumer_secret)。因此,为了弥补 1.0 的“先天不足”,2010年5月开始,OAuth 正式进入 2.0 这一“伟大的”时代。

关于OAuth 2.0

2012年10月,OAuth 2.0 正式发布为 RFC 标准协议 RFC6749

OAuth 2.0 是什么?

OAuth 2.0是一种开放的、标准的访问授权(Authorization)协议,是 OAuth 协议的下一代版本。OAuth 2.0 关注客户端开发者的简易性,同时为 Web 应用、桌面应用、手机和智能设备提供专门的授权流程,在业内得到广泛应用,它也率先被 Google、Microsoft、Facebook、Okta 等各大厂商应用于实际的用户授权访问的业务场景中或者基于此协议对外提供服务。它的制定就是为了解决资源的访问安全性(access_token)以及授权灵活性(scope)的问题,OAuth 2.0 能够使得应用对资源的访问更加安全,对开发者来说更加友好。OAuth 2.0与1.0有以下区别:

  1. OAuth 2.0 版本定义了四种角色,将授权和资源访问服务独立开,使职责划分更加明确;
  2. OAuth 2.0 版本定义了四种基本的授权流程,根据不同的场景和端,使得开发者可以自由选择相关授权流程,适配了客户端、App 端、PC 端等多种场景;
  3. OAuth 2.0 版本废弃了 1.0 中的 token 必须存在一个对应的 secret 的设计,2.0 推荐采用 SSL 的方式确保网络传输的安全性,且 Token 一经签发可以直接使用,由更加安全完善的前置授权流程确保 Token 的可用性和安全性,这一设计也就导致 2.0 无法兼容 1.0 的老版本;
  4. OAuth 2.0 版本基于授权码模式(1.0 的 Request Token 也可以理解为一种复杂的授权码模式),延申出了 PKCE 模式,使得无服务端的 web 应用也可以直接使用授权码模式而不用担心 secret 的泄露。

OAuth 2.0 包含哪些角色

  • Resource Owner(RO) 资源所有者,指可以对受保护资源进行授权的实体。当资源所有者是一个人时,它特指一个终端用户,对应 1.0 中的 User;
  • Resource Server(RS) 资源服务器,托管受保护的资源信息,可以接收带有访问令牌的受保护资源的请求,并进行响应,对应 1.0 中的 Service Provider;
  • Authorization Server(AS) 授权服务器,验证用户身份然后为客户端颁发资源访问令牌,对应 1.0 中的 Service Provider;
  • Client 客户端,代表意图访问受限资源的应用程序,对应 1.0 中的 Consumer。 注:RS 和 AS 在逻辑上是分开的,但在一般场景下,RS 和 AS 泛指同一个服务,即这个服务即为 RO 颁发 token 又存储被保护的资源。

OAuth 2.0 包含哪些授权方式

授权码模式(Authorization Code)


技术咖 | 全面了解 OAuth 协议(一)

图四:OAuth 2.0 授权码模式授权流程

这种方式是最常用的流程,也是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样可以避免令牌泄漏。

简化模式(Implicit)

技术咖 | 全面了解 OAuth 协议(一)

图五:OAuth 2.0 隐式模式授权流程

这种方式不通过第三方应用程序的服务器,RFC 6749就规定了这种方式允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit),一般移动 APP 端多选用这种方式。

密码模式(Resource Owner Password Credentials)

技术咖 | 全面了解 OAuth 协议(一)

图六:OAuth 2.0 密码模式授权流程

用户向客户端提供自己的用户名和密码。客户端使用这些信息,向“服务商提供商”索要授权。在这种模式中,用户必须把自己的密码给到客户端,但是客户端不得储存密码(建议)。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

客户端模式(Client Credentials)

技术咖 | 全面了解 OAuth 协议(一)

图七:OAuth 2.0 客户端模式授权流程

指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个应用共享同一个令牌。

关于OAuth 2.1

OAuth 2.1 是什么?

OAuth 2.1 规范草案的作者之一 Aaron Parecki 在一篇博客文章中描述” My main goal with OAuth 2.1 is to capture the current best practices in OAuth 2.0 as well as its well-established extensions under a single name. That also means specifically that this effort will not define any new behavior itself, it is just to capture behavior defined in other specs. It also won’t include anything considered experimental or still in progress.

简而言之,OAuth 2.1 的出现不是为了推翻 OAuth 2.0 或者完全替代 OAuth 2.0,相反的,OAuth 2.1 的出现,在基于 OAuth 2.0 的基础上,极大地增加了 OAuth 协议的健壮性和扩展性,正如 Aaron Parecki 所说,OAuth 2.1 并不会包含任何被认为是实验性或者尚未落地的东西。

OAuth 2.1 主要做出的改变如下:

  • 授权码模式使用 PKCE (RFC7636) 中的功能进行了扩展,因此根据此规范使用授权码模式时需要默认添加 PKCE 相关的参数;
  • 必须使用字符串的精确匹配方式来比较 redirect_url;
  • 本规范中省略了隐式授权模式和密码模式;
  • access_token 不建议通过 GET 方式在 URL 中传递,开发者熟知的 Github 在 2019 年底开始就施行了这一规则,参考相关介绍:GitHub is deprecating authentication to the GitHub API using query parameters;
  • 刷新令牌必须受 AS 限制或一次性使用;

未完待续~

接下来技术咖还会就 OAuth 协议的流程细节、应用场景、各种混合解决方案等进行一一剖析。另外,各位读者朋友,如果有相关问题,欢迎评论区留言,我们会对优质的留言专门汇总后整理成详细的问答类文章分享给大家。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章