理解OAuth2授权模型

预计阅读耗时:20min

1. 问题的引入

在客户端-服务器通信过程中,服务提供者通常都保存着客户的数据资产,比如你的个人信息、外卖订单、聊天记录、消费记录等,显然这些数据是非常私密且敏感的。作为提供服务的一方,服务端对这些数据具有保密性的义务,本质上它们属于用户的个人资产。因此服务提供者需要在客户端访问这些数据时,验证访问者确实是这些数据的拥有者。

常见的验证方式有用户名/密码、生物特征识别等方式。

有时候,我们希望在某些APP上,通过授权去获取另外一些APP的个人数据,比如某第三方APP获取你的微信头像、昵称,用于该APP的微信登录和个人资料展示。

作为个人资料的持有者,微信平台如何在不泄露你的密码的前提下,为可信的第三方APP提供你的个人数据,就是OAuth2需要解决的问题。

2. Oauth2的基本概念

为了方便,我们将资源所有者(Resource Owner)称为第一方,资源服务器(Resource Server)称为第二方,那么,在问题模型中,最初只有资源所有者(Resource Owner)和资源服务器(Resource Server)参与。资源服务器基本只能通过用户名/密码来校验用户身份。

实践中资源所有者(Resource Owner)通常不会直接发起请求,而是通过某些第三方载体,通常是某些期望使用资源所有者数据的app,比如微信登录网易云音乐时,网易云音乐就是第三方,也就是我们基本概念里的应用程序(Client)

同时,由于授权本身与业务属于不同域的问题,可以将授权能力独立解耦为授权服务器(Authorization Server),这样资源服务器(Resource Server)就可以将主要精力放在对数据的管控和传输上。当然对于小规模的应用来说,集成在一起也没有问题。

这就构成了OAuth2需要的基本实体

  • 资源所有者(Resource Owner):资源的实际拥有者,通常指C端用户
  • 资源服务器(Resource Server):资源的实际持有、维护方,它支持对C端用户数据的增删改查,负责保护C端用户数据
  • 应用程序(Client):期望获得资源服务器上C端用户数据的一方
  • 授权服务器(Authorization Server):授权服务器,管控对C端用户资源访问授权的一方

其中,授权服务器是上面的问题中没有介绍的,而授权服务器是OAuth2解决这些数据授权问题,而引入的一个关键概念,我们在后面会展开来说。

除此之外,还有几个概念读者需要理解:

  • AccessID:有些平台称作AppID或AccessKey,它是由授权服务器(Authorization Server)预先颁发给应用程序(Client)的一个标识key
  • Secret:有些地方还叫AccessSecret等,它与AccessID构成一个密钥对儿,通常用来签名、验签,授权服务器(Authorization Server)用AccessID来唯一标识一个应用程序(Client)
  • AccessToken/RefreshToken:访问Token/刷新Token,应用程序千辛万苦地执行授权过程,就是为了换取这个东西,它唯一地标识用户信息,一旦获取到AccessToken,就可以访问该AccessToken代表的用户数据。为了避免token的滥用,AccessToken通常被设计为有时效的,续期时需要携带RefreshToken换取更新的AccessToken。

很多介绍OAuth2的文章没有说明这几个概念,如果你使用过任何开放API,上面这几个概念应该已经不陌生,如果你不理解这几个概念,建议随意找一个开放API来对接一下试试,因为这对于理解OAuth2至关重要。

3. Oauth2的授权模型

OAuth2支持四种授权模型,其中授权码模式是完整的OAuth2模型,其他模型都是该模型的变体。

  • Authorization Code - 授权码模式
  • Implicit - 隐式授权模式
  • Client Credentials - 客户端凭证模式
  • Resource Owner Password - 资源所有者凭证模式

3.1 在开始之前

在开始介绍OAuth2之前,有必要进行一些准备工作。

3.1.1 应用程序(Client)

作为应用程序的开发者,如果希望自己的app可以使用微信登录,那通常你需要在微信平台上注册开发者,并拿到自己的AccessID和Secret,AccessID和Secret通常通过某种算法生成,比如SHA256,通常他们是一串字母+数字。这不重要,重点是当你调用微信的授权服务器(Authorization Server)API时,这将作为微信平台识别应用程序(Client)的唯一标识。

3.1.2 授权服务器(Authorization Server)

作为授权服务器(Authorization Server)的开发者,为了实现OAuth2授权能力,授权服务器(Authorization Server)需要实现这几个API

方法名用途调用者
/get_code颁发授权码code资源所有者(Resource Owner)
/get_token_by_code用code换取token应用程序(Client)
/verify_token验证token合法性资源服务器(Resource Server)

至此准备工作基本算完成了。

请时刻记着我们最开始引入的问题,以及解决这个问题的所面临的问题:

即如何在避免应用程序(Client)接触资源所有者(Resource Owner)密码的情况下,通过用户授权获得用户的数据。

3.2 授权码模式

授权码模式是OAuth2里最完整的授权模型,四个实体都参与其中。下图表示了四个实体参与授权的过程,其中箭头的起点为主动发起请求的一方,终点为请求的处理和响应方。

过程如下:
【开始 => 用户在APP发起登录 => APP引导用户跳转至授权页面 => 用户点击同意授权 => 授权页面返回授权code并引导用户跳回APP => APP用授权code向授权服务器发起token请求 => 授权服务器生成并返回token给APP => APP使用token向资源服务器发起获取用户数据请求 => 资源服务器请求授权服务器验证token => 资源服务器返回用户数据给APP => 结束】

提炼一下,这里基本只包含三个与授权服务器(Authorization Server)交互的关键过程:

  1. 资源所有者(Resource Owner)授权获得授权码(Authorization Code)
  2. 应用程序(Client)使用授权码(Authorization Code)换取令牌(access_token)
  3. 资源服务器(Resource Server)验证令牌(access_token)

我不打算详细的介绍这个过程的实现细节,包括RFC在内,有很多类似的内容可以检索到,理解OAuth2的重点在于,它为什么这么设计。所以接下来看下,如果不这么做,会发生什么。

3.2.1 资源所有者(Resource Owner)授权获得授权码(Authorization Code)

应用程序(Client)可不可以直接申请令牌(access_token),省掉授权码(Authorization Code)获取的过程呢?

不可以

因为这是用户的显式授权过程,虽然APP可以代用户发起该授权操作,但从授权服务器的角度来看,无法判断这个请求是用户自己发起的,还是第三方APP主动发起的。也就是说,如果允许应用程序(Client)未经用户允许申请令牌(access_token),那就表明应用程序(Client)可以在资源所有者(Resource Owner)不知情的情况下,恶意地获取用户数据。而获取授权码的过程,就是一定需要资源所有者参与。

类似我们在微信登录时,登录页会跳转到微信的授权界面,你必须显式地点击同意授权,微信的授权服务器才可以确认是你亲自发起的授权请求。支付宝的扫脸认证也是一个道理。如果你没有当面把钥匙交给一个代理人,那么当这个代理人进入你家时,ta就是非法的。

3.2.2 应用程序(Client)使用授权码(Authorization Code)换取令牌(access_token)

既然授权码(Authorization Code)不可以省略,那这里应用程序(Client)拿到授权码后,能否使用授权码(Authorization Code)完成后续的数据读取操作呢?

不可以

其实也不是不可以,毕竟是授权服务器自己颁发的授权码,自己哪能不认呢。但是,这个过程中风险最大的环节在用户侧。

考虑第三方APP是web应用,那么授权码在跳转到应用程序(Client)时,需要被浏览器处理,作为授权系统的设计者,你无法预知用户浏览器中,是否有可以窃取到授权码的风险代码。一旦授权码被窃取,那么攻击者就有可能通过该授权码获取到用户敏感数据。而第三方应用程序(Client)通过授权码换取令牌(access_token),基本是在服务端进行的,安全等级显然要比用户侧高得多。

也正是因为这个原因,授权码(Authorization Code)在RFC中被约定为只能使用一次,这样一旦授权码泄露了,授权服务器(Authorization Server)立刻可以感知到。事实上用户也会立刻感知到,因为通过授权码换取令牌的时间往往在短暂的几秒内,窃取者必须尽快利用code以防其失效,一旦code被使用过一次,应用程序(Client)使用授权码换取令牌的请求会失败,用户也会登录失败。

在OAuth2的RFC中,对授权码的生命周期做了如下描述

The authorization code MUST expire shortly after it is issued to mitigate the risk of leaks. A maximum authorization code lifetime of 10 minutes is RECOMMENDED. The client MUST NOT use the authorization code more than once. If an authorization code is used more than once, the authorization server MUST deny the request and SHOULD revoke (when possible) all tokens previously issued based on that authorization code

攻击者如果想获取用户数据,需要截获用户的授权码、窃取到该APP服务端的AccessId/Secret、并在极短的时间内,抢在APP服务端之前发起获取用户资源的请求。尽管还是可能存在风险,但通过授权码换取令牌的过程,已经将用户数据的泄露风险极大地降低了。

3.2.3 资源服务器(Resource Server)验证令牌(access_token)

这一步能不能省去呢?

显然不可以

在应用程序(Client)向资源服务器(Resource Server)发起的数据读请求中,AccessID和Secret只能代表第三方APP自己的身份,第三方应用程序(Client)可能会有很多用户同时使用,而每个令牌(access_token)都关联着唯一的C端用户的数据权限,如果不校验令牌,资源服务器(Resource Server)就无法知道,第三方APP是要获取哪个用户的数据。所以显然,这里不能省略access_token的校验过程。

所以,授权码模式下,看似繁琐的过程却都是必要的。也正是因为这个“繁琐”的过程,确保了用户数据的安全。

3.3 其他情形

有凡是就有例外,下面简单说下隐式授权、客户端凭证、资源所有者凭证的OAuth2模型。一旦你理解了授权码模式,那么下面几个模式就是小菜。

3.3.1 隐式授权模型(Implicit)

来考虑纯前端的应用,例如完全使用JS编写的应用,在没有服务端参与的情况下,当用户登录时,请求直接从前端跳转至授权服务器授权页面。用户确认授权后,授权服务器将颁发token给前端应用程序。显然,这种形态的应用,授权服务器无法通过Secret校验应用身份(浏览器端存储Secret存在安全风险),token也有可能暴露在浏览器代码中,所以说这是OAuth2中最不安全的授权模型也不为过。

正因为如此,OAuth2中还有一部分规范,是用来约束授权范围的,即用户数据范围、读写权限等,通过这类限制条件,可以降低隐式授权带来的安全风险。实践中当用户需要进行高安全级别操作时,可以引导用户下载APP,并在APP中通过授权码模式完成高安全级别的操作。

3.3.2 客户端凭证模型

这里的客户端不是指APP的用户,而是指某些应用服务。以大数据分析服务为例,APP开发者希望通过读取并分析海量用户数据,来优化自己的服务质量。在这种情况下,不大可能让用户参与授权码模式中的显式授权过程(实际上这类授权通常都添加在隐私协议中)。大数据服务器在获取用户数据时,会携带AccessId直接向授权服务器发起授权请求,在获取到token后,向资源服务器发起读数据请求。

对于C端用户,平台的账号/密码是数据权限的根基、而对于应用服务来说,AccessId/Secret就是其访问数据的关键参数,其保密性也比单个用户的账号/密码重要的多。当授权服务器验证该应用服务器传入的token时,AccessId合法性的校验至关重要。因为它代表了发起请求的服务端应用程序,是否是该平台的一个合法账号,这决定了它能否访问该AccessId关联的海量C端用户数据。

3.3.3 资源所有者凭证模型

最后一种授权模型是,也是唯一一种涉及到用户密码的场景。即C端用户通过账号/密码登录APP,APP使用C端用户的账号密码+自己的AccessId/Secret,向授权服务器发起获取token的请求。由于APP可以直接拿到用户的密码,你应该敏感地意识到问题的严重性。

OAuth2只建议在传统账密模型向OAuth2模型迁移的过程中采用这种方式,在RFC中也申明了这种授权方式的风险。

This grant type carries a higher risk than other grant types because it maintains the password anti-pattern this protocol seeks to avoid. The client could abuse the password, or the password could unintentionally be disclosed to an attacker (e.g., via log files or other records kept by the client).

4. Conclustion

上面简要介绍了一下OAuth2中四种授权模型,以及他们的应用场景。如果要实现这样一套授权模型,还有很多细节工作要做,比如你的accessid/secret要如何生成、基于accessid和secret的加解密/签名规则设计、授权码和token的生成/存储/快速检索,以及关联的用户数据模型设计,但这些不在OAuth2的讨论范围内。总之OAuth2是提供了这样一种可能,即在避免应用接触用户账密的情况下,完成范围可控的授权。

授权码模式是最完整、最严格的授权模式,它假定了资源所有者、应用程序的不安全性,尽最大可能保障授权过程的安全。而其他三种模式,要么是依赖对资源所有者或应用程序是安全可信的作为一定的前置条件,要么一定程度上牺牲安全性来迎合系统设计的需要,因此需要谨慎采用。

参考
1. 白话让你理解什么是oAuth2协议
2. The OAuth 2.0 Authorization Framework(RFC6749)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值