全面详解互联网企业开放API的 “守护神”

前言

这篇文章前前后后写了两个多礼拜,也是自己第三次写4000字以上的技术单篇文章。写作过程先是根据自己的思考和资料查找确认,再结合宙斯开放平台的实际使用,每天中午吃过饭一个小时来将这些内容碎片化的记录下来,今天得以利用整块的时间梳理总结完成。

研究Oauth这个方向的原因,第一是我本身就在做开放平台相关的工作,我需要对这一技术有个更深的了解;第二是今年618值班期间对线上开放安全的谨慎心理促使我去上网查找有关Oauth安全使用。

当时发现了一本正在预售的《Oauth2实战》这本书,心想关于Oauth的技术居然也有人写成了一本书,那时候第一反应是,只要有了方向就有了厚度。这也更加坚定了我深入研究这种技术的信心。

在7月初拿到这本书之后,整个7月份和8月初我都在反复的来翻阅这本书。本文的部分内容当然也有对这本书的总结和思考。

正文开始

Oauth的历史起源

回顾历史,在2007年以前所有的WEB服务公司,包括现在我们已经知名的Twitter和Google等互联网巨头企业,这些大的互联网公司都有很多个系统,如果用户需要在这些系统之间无缝浏览,大部分都是通过一种叫做分布式认证OpenID的方式来实现。再进一步如果他们要想让第三方应用访问自己的API都是利用各自的专有实现,因为OpenID的方式只是一种认证,解决了“是不是”的问题,它无法将用户名和密码用于API来解决“可不可以”的问题。

为了解决这个问题,当时一些开发人员尝试发明一种协议,可以允许用户对API访问授权。期望的是让第三方应用只要获得用户的授权并得的一个访问令牌,就能使用这个令牌来访问API。在2007年12月Oauth Core 1.0发布,象所有的新生事物一样,Oauth1.0也有它不完善的地方,其中就包括被发现会话固化攻击的漏洞,直到2009年6月发布了Oauth Core 1.0 Revision A版本彻底解决了这一问题。

在紧接着的2010年5月互联网工程任务组(IETF)发布了Oauth 2.0的草案。在2012年IETF最终批准了Oauth 2.0 核心规范,我们现在所有使用的Oauth技术都是基于这一规范。Oauth2.0对比Oauth1.0最大的区别,除了安全性进一步提高之外,还有一个重要本质区别,Oauth2.0是在基础层面提供了设计规范,也就是说我们只要遵守了这种规范就可以灵活地应用到我们现实世界的各种场景。

现在Oauth2.0早已经是互联网上首选的授权协议,无论大型互联网企业还是初创型企业,都在广泛的使用这一协议。

最标准的Oauth2流程

Oauth的最原始背景就是解决WEB应用下的授权的安全问题,因此一定不能缺少浏览器的参与。对于非标准条件下的Oauth流程在后续的文字中我们会有讲述,那个时候可能有的场景是不需要浏览器的。现在我们在叙述的是标准场景下的Oauth使用。

Oauth组件间的通信包括前端通信和后端通信,前端通信就是组件之间的需要交互的信息数据在浏览器里面流转,后端通信就是组件之间需要交互的数据信息通过WEB SERVER之间流转。实际上只有标准场景下的Oauth2流程才会既使用前端通信又使用后端通信,这点在介绍非标准场景下的Oauth使用的时候也会去分析,大家先记下来。

资源所有者A要授权正在使用的第三方软件来能够访问A在平台上受保护的资源,那么A通过浏览器首先访问的是第三方软件的URI地址,此时第三方软件遵循Oauth2.0的协议并按照平台的要求拼接授权URL,将用户引导到平台的授权页面,这个时候发生了第一次URI重定向。

A点击了授权页面上的授权按钮,平台一方的授权服务器会对当前的用户进行身份验证,如果身份合法会生成一个CODE也就是我们常说的授权码,然后将这个CODE重定向回第三方软件的CALLBACK URI上(这个CALLBACK URI是拼接授权URI的时候就传过来了),这个时候发生了第二次URI重定向。

第一次重定向好理解,用户在使用浏览器访问第三方软件的URI地址,第三方软件需要做引导。第二次重定向为什么也需要呢,通过WEB SERVER直接OUT PRINT回第三方软件的服务器不就可以了吗,如果仅仅是返回这个CODE值当然可以,而且这样还更安全。但是不要忘记了用户还在浏览器上面等着呢,如果将CODE的值直接写回到第三方软件的WEB SERVER上,就会把浏览器上的用户旁路了,因此还必须进行第二次重定向。至此获取CODE的流程都是通过前端通信进行交互的。

在第三方软件获取到CODE之后,同样遵循Oauth2.0的协议并按照平台的要求,会发起一个HTTP POST请求到授权服务器,去访获取ACCESS TOKEN(访问令牌),这个HTTP请求中包含了平台一方事先给第三方软件分配好的client_id和client_secret,这样ACCESS TOKEN的数据传递就是在两个WEB SERVER之间的交互了。至此获取访问令牌的流程是通过后端通信进行交互的,另外再加上HTTPS的保护,ACCESS TOKEN的获取变得更安全了。

以上交互通信如下图所示。

我们还会思考另外一个问题,为什么标准流程下需要先获取CODE,然后再通过CODE获取ACCESS TOKEN呢?这个原因可以结合前端通信环节中的必须经过两次浏览器重定向的描述,如果没有获取CODE这个流程,直接将ACCESS TOKEN重定向回浏览器,无疑这会将访问令牌暴露出去带来安全上的问题。

非标准的Oauth2流程

如果将授权码的方式认定为是标准授权流程的话,那么除此之外的各种方式的授权流程都可以称之为非标准授权流程。其实支持非标准流程的授权也正是Oauth2.0的伟大之处,因为我们构建的应用程序都是要满足现实世界的需求,立足于各种生产实际环境的,授权当然也需要适应各种现实环境的变化。这样的适应性也正是Oauth2.0在某些关键点上的灵活性体现,它提前预见了一些现实需求世界的变化。

那么现实需求世界中非标准流程的应用都有哪些呢。

1-没有服务器的应用

比如JavaScript应用,这种应用本身将自己嵌入到了浏览器内部,没有也不需要服务端的支持。此类应用不适合使用标准的授权码方式的Oauth2流程。根据先前介绍的授权码流程我们得知,因为通过CODE换取TOKEN的各个步骤中浏览器和应用之间的信息是隔离的,也就是说浏览器接触不到由应用控制的信息,应用也不知道浏览器的状态。但是当应用嵌入到浏览器之后,应用的所有操作和信息对浏览器都不再有保密性可言了,因此通过CODE这一步也就没有实际意义了。

此类型的应用在请求授权服务器之后会直接得到一个授权码TOKEN,官方把这种方式称为[隐式许可类型]。而且在换取授权码的时候也无需传入秘钥secret的值,同理对于在浏览器中secret的值已经无处藏身了。缺少了对应用的身份认证之后,当然在这种方式下安全性会大打折扣,这是我们需要注意的事情。

2-没有授权用户的应用

我们知道授权的动作都由有资源的拥有者发起的,但有一种应用可能是没有明确的资源拥有者了,或者说这个应用本身就是资源拥有者,应用和资源拥有者"合二为一"了。这类应用一般都是原生应用。这类应用我们可以直接通过clientid和secret去换取token的方式处理授权,官方把这种方式称为[客户端凭据许可]。

不过在Oauth2.0里面,对于类似"应用就是资源拥有者"这样的情况,也提供了可以使用Oauth流程的支持,这样Oauth的授权流程便具有了统一性。另外还有一点区别需要我们能够认识到,clientid和secret是解决开发者能够调用平台的问题,Oauth2是解决开发者可以获取用户数据的问题。

这种类型的应用也可以由应用自身发起CODE请求,再传输CODE换取TOKEN。我们可能会意识到这不是标准的授权码流程么,可是我们现在说的不是非标准流程的授权方式吗。因为在真正标准的授权码流程中的发起方是资源拥有者而不是应用本身,先前我们也介绍了Oauth2.0兼备了这种类型应用也可以使用授权流程的统一性。这里的非标准实际上是指发起CODE请求的不再是资源拥有者而是换成了应用。这种方式我们也可以称为[类授权码许可]

小程序中的使用

1-支付宝小程序

开发者在开发小程序需要接入授权流程的时候,这种情况下仍然是需要两个步骤,分别是获取CODE和ACCESS_TOKEN。只不过小程序的环境下所采取的方式跟WEB环境下获取的方式不同。以支付宝小程序举例,如下图所示,第一步获取CODE的时候是通过JS API的方式调用支付宝APP的本地方法,第二步获取ACCESS_TOKEN是通过开发者自己的服务器去请求到授权平台,发请求的时候附带上第一步的CODE值。

2-微信小程序

那么微信小程序采取的授权流程又是怎样的呢,我们到微信小程序官方文档上去搜索会发现,微信的规范是要求开发者采用客户端凭据的方式来获取调用后台接口的凭据ACCESS_TOKEN。如下截图所示,

微信小程序的授权方式实际上是Oauth2.0多种授权方式的其中一种,叫做客户端凭据方式。从请求参数中的授权类型这个参数可以看到grant_type的值为client_credential。这种授权方式就没有了授权码CODE或者其它任何换取TOKEN的临时凭据。

向授权服务器进行身份认证的请求参数是appid和secret,一个是应用的ID,一个是应用被分配的秘钥。同时客户端凭据这种授权方式中也不会有刷新令牌,因为授权系统会认为客户端可以随时获取新令牌。

不过需要注意的一点是,微信小程序也有个授权,根据微信官方文档可以得知wx.authorize(Object object)这个授权并没有返回我们熟知的Oauth里面的CODE等兑换TOKEN的凭据。

实际上这个授权是让第三方小程序能够获取到微信APP里面的功能以及手机本身的功能,比如拍照、录音等。如果是需要发起HTTP请求调用的API是需要通过上文说的客户端凭据的方式。

但微信给我们返回的数据都是加密的,解密的时候需要我们登录的时候的SESSION_KEY来作为秘钥解密,当然每个登录用户的SESSION_KEY是不一致的。

Oauth面临的安全风险

在Oauth的发展历史上,最大的"安全风险漏洞"事件,可能是2014年被新加坡的一位大学学生发现的了,据记载当时可谓影响了全世界的互联网公司,腾讯、阿里巴巴、支付宝、搜狐、网易、人人网、开心网、亚马逊、微软、eBay、Facebook、Google、雅虎等等这些巨头们都受到波及。但后续证明这个当时被称为"隐蔽重定向漏洞",并不是Oauth本身的漏洞。

实际上Oauth的使用方没有按照Oauth的标准去接入,涉及到的上述公司均反馈是出现在他们平台上的第三方系统,而不是公司的自有站点。这也就是刚开始我们描述安全风险漏洞的时候为什么加了引号,我们说那一次引起轰动的"漏洞"是没有按照标准接入的原因正是忽略了回调URL校验

我们来看下发生这个问题点的位置,如下图所示。在黑色圈里面的回调URL是开发者在入住开放平台的时候填写的,如果授权码流程的第一个重定向被恶意用户篡改了callback的值,授权服务器又没有跟注册时候入库的值做校验,在第二次重定向的时候就会把数据发送到了恶意用户所指定的服务器上面去了。

那么如果不按照Oauth的建议规范去接入使用,还有哪些安全风险呢,比如CSRF(跨站伪造)就是其一。让我们一起来看下面这张图,黑色圈这个位置就是被恶意攻击者攻击的位置。如果将code值伪造成一个非法的值,那么授权服务就遭受到token暴力搜索。如果将Oauth作为认证来使用,那更将是一个灾难。

应对策略就是在第三方应用请求code的url上增加state参数,并把state的参数值保存起来,授权服务器发起重定向请求的时候再把state参数值带过来,第三方应用会做一个state值比对判断,如果两个state的值不相同,则有可能发生了被伪造攻击。

关于Oauth的安全上面只是列举了其中一个方面,自从Oauth1.0升级到Oauth2.0之后,Oauth实际上从单体的变成了模块化,比如有客户端(第三方应用)、授权服务器、受保护资源,这三个模块每一个都面临着因使用Oauth规范不当而出现的安全风险问题。

总结

开放平台的基础技术需要 “两条腿” 能够跑起来,这两条腿分别是网关和Oauth。网关可以让内部的API被外部调用,Oauth可以保护这些被调用的API。Oauth是一种协议,也是一种工具。Oauth本身是属于安全范畴的,它管理者API的访问权限,守护者重要的数据。

本文从Oauth的起源历史开始说起,先后介绍了Oauth的授权码许可流程和其它非标准流程的示例,还介绍到了支付宝小程序和微信小程序中的授权。最后又介绍到了Oauth所面临的安全风险,列举了两个安全风险的例子以及这种安全防范的应对措施。

reference

https://docs.alipay.com/mini/introduce/authcode 支付宝小程序授权

https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html 微信小程序授权

本文分享自微信公众号 - 猿天地(cxytiandi)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券