OAuth 2.0 简述

本系列文章的思想,都融入了 让 Java 再次伟大 这个全新设计的脚手架产品中,欢迎大家使用。

什么是授权

生活中有很多授权的场景,比如:假设你有一辆车,你现在指派一名代驾司机来帮你把车开回家,这就是一种授权。因为你授予了代驾司机使用你的小汽车的权利。在软件领域,授权通常指用户对某软件授予访问受保护资源权限的行为。OAuth2 就是为了将这种行为标准化所设计出来的协议。

OAuth 不是什么

从上面的内容可以了解到:OAuth 是用户对软件行为授权的协议。用户除了对软件授权以外,当然也可以对用户授权——比如我授权 cherry 全权操作我的基金账户。OAuth 2 当然能够解决这样的问题(请查阅基于 OAuth 衍生出的 UMA 协议相关资料),但是需要记住这不是 OAuth 设计的初衷。
再比如,现在有不少系统利用 OAuth 来构建统一登陆中心(SSO),他们错了吗?当然没有。用 OAuth 解决「认证」的问题也是小菜一碟。但是就像刚才说过的, OAuth 本质上只是一个授权协议。

什么是令牌

接上例。显而易见,代驾司机需要车钥匙才能帮你将车开回家。当然你可以将车钥匙交给代驾司机,但是显然让代驾司机使用专用的「泊车钥匙」显然更加安全。在软件领域,你的账号密码就相当于车钥匙,令牌则类似泊车钥匙。当然,并不是所有企业都拥有泊车钥匙。但是拥有泊车钥匙的汽车通常都意味着更好的品牌、更高的售价、以及更棒的质量。

授权码许可类型的协议流程

OAuth 协议的特点是,只定义交互流程但不对流程中的接口交互参数等细节做过多限制。这句话有点难以理解,但是以后你会懂。
下面用一张图来展示 「授权码许可类型」 的授权流程。等等,既然有「授权码许可类型」那还有没有其他类型?当然有,比如后面会提到的「隐式许可类型」。不过我们现在先来看看授权码许可类型吧。

  • 由于授权服务器需要提前准备客户端元数据(先不要问为什么),所以使用 OAuth 的第一步是将客户端注册到授权服务器中。
  • 浏览器请求客户端(照片打印服务)打印谷歌照片中存储的用户隐私照片。很显然,谷歌照片服务中的数据不允许客户端随意访问,这时候就需要找谷歌授权服务器对客户端进行授权,这一步是客户通过 302 重定向来实现的。以下示例中的 state 是一串随机生成的参数,就像随机生成一串订单号一样。
HTTP/1.1 302 Moved Temp
Location: http://google.auth.com/authorize?response_type=code&scope=foo&client_id=mrpicture&redirect_url=http://mrpicture.com/callback&state=Lwt50024XyzHW1
  • 上一步通过 302 使浏览器向授权服务器发送了一个 Http 请求。因为我们在第 0 步提前进行了合法客户端的元数据配置,所以这里可以通过校验 client_id 与 redirect_url 来过滤无效和伪造的客户端请求。接着授权服务器会渲染授权页面要求用户登录并授权对应权限给客户端。
  • 当用户授权完毕后,授权服务器保存权限范围(scope)并创建授权码。由于授权是用户浏览器直接和授权服务器进行的,所以需要使用 302 + redirect_url 回调客户端并告知用户授权成功并回传授权码与 state 参数。
HTTP 302 Found
Location: http://mrpicture.com/callback&state=Lwt50024XyzHW1&code=8V1qosiJswxv&response_type=code&scope=foo&client_id=mrpicture
  • 客户端收到回调后,验证 state 等参数是否与自己前面步骤发送的参数一致,这可以防止攻击者随意编造请求访问客户端回调地址引发后续流程,浪费客户端和授权服务器资源。然后使用 HTTP 基本认证,将在元数据注册步骤中获取到的 client_id 与 client_secret 与授权码一起发送到授权服务器,获取访问 token。最后记得不要忘了 grant_type 字段。授权码许可类型的 grant_type 总是固定为 authorization_code。
POST /token
Host: http://google.auth.com/
Content-type: application/x-www-form-encoded
Authorization: Basic b2F1dGgt2xpZW50LTE6BeFFJIWxxyliit220xCssXPsiwn5Bcod2a(base64(urlEncode(account+":"+password)))

grant_type=authorization_code&redirect_uri=http://mrpicture.com/callback&code=8V1qosiJswxv

为什么这里还要发送 redirect_url 呢?根据 OAuth 规范,如果你在授权请求重定向中指定了 URI,那你在令牌请求中也必须指定。如果一切顺利的话,客户端就可以获得一个 Bearer 令牌。Bearer 令牌中还可以包含刷新令牌和令牌的权限范围以及过期时间等等。

"access_token": "2983Exodengdisowagds2q2345dfsoiwg",
"token_type": "Baerer"
  • 既然获取到了令牌,客户端就可以使用令牌请求相关资源并返回到浏览器,或者保存令牌在客户端供今后使用。

再稍微深入一点

之前我们讲到过,OAuth 2.0 协议的特点是定义交互流程但不对交互细节作过多限制。所以想要设计一个健壮的 OAuth2.0 协议光理解上面的流程图是不够的。如果你还没有丧失兴趣,建议继续往下阅读。

前端信道和后端信道

由于授权一定伴随到第三方网站上登陆的动作,若将登录后的 token 直接通过浏览器 url 返回是非常不安全的行为。另外 url 长度有限也不适合放入复杂的 token 信息。所以,通过首先发放授权码,其次请求客户端通过授权码在后端换取 token 是更安全的做法。
回到协议流程图中的第 2 步和第 4 步。在这一步用户通过浏览器重定向和授权服务器建立了授权链接并通过浏览器回调发放了临时授权码,通常我们将这种流程称之为前端授权信道,简称「前端信道」。在第 5 步客户端收到了授权服务器的回调,并将自身的凭据和授权码一并发送到授权服务器获取 access_token,这通常被称为后端令牌信道,简称「后端信道」。
一个完整的授权码协议流程,一定包含一个前端信道和一个后端信道,不然就不是标准的授权码协议流程。

针对用户的 CSRF 攻击

接着上一小节来分析第 2 步。用户和授权服务器通过前端信道建立连接后,服务器会渲染或者 foward 一个授权页面给到用户,这看起来好像没什么问题。但是使用 CSRF,攻击者可以很容易的使用户反复授权浪费服务器资源(反复发送无效授权请求),或者在某些糟糕的授权 API 设计上将用户资源授权到攻击者客户端。

好消息是使用一个随机令牌就可以防范 CSRF 攻击。授权服务器渲染或者 foward 授权页面时,生成一个随机 token 保存到服务器,再将这个令牌渲染到页面并要求用户在后续的授权 API 中带上这个参数,这样就保证了每次授权都可以和一个合法的授权重定向关联起来。简而言之,通过授权服务器和客户协商一个授权口令,使后续的获取令牌的请求 可以通过该口令证明自己的身份。

针对客户端的 CSRF 攻击

同样还有针对客户端的 CSRF 攻击。假设攻击者首先通过授权服务器进行授权获取到属于他自己的授权码,接着通过 CSRF 诱使受害者(资源拥有者)访问带有攻击者授权码的客户端回调地址的话,就会将攻击者的授权上下文和资源拥有者的客户端绑定在了一起。(即当前客户端网站的实际登陆者是真实用户,但授权码属于攻击者,这使得后续的交换 token 的流程使用了攻击者的授权码,导致授权服务器和客户端识别的用户不同,从而发放的 token,访问的资源发生错误)。
和主流的防范 CSRF 攻击的方式一样:在流程中增加使用 state (发放时授权码绑定一个唯一的回调参数)参数,即可避免这种情况的发生。

会话劫持

在大多数的 OAuth 的最佳实践中,第 5 步换取 access_token 的接口一旦接收到一个包含授权码的请求后,就会把这个授权码置为无效。为什么要这样做呢?由于 OAuth 发放授权码是通过 302 重定向来进行的,所以在公共计算机上,任何人使用过的授权码重定向都会留存在历史记录中。如果攻击者在客户端上用他自己的身份凭据登录,然后找出之前的授权码进行重定向,这样攻击者就可以使用上一个资源拥有者的资源。

隐式许可类型流程

相较于授权码许可类型,还有一种常见的许可类型称为隐式许可类型。这种许可类型没有客户端,或者说用户就充当了客户端——比如用户直接访问授权服务器进行认证并授权,授权服务器在授权端点会直接生成一个令牌并返回到浏览器。

隐式许可类型一般适合于完全运行于浏览器 js 内的应用或者原生开发的 app 等场景上。另外由于隐式许可类型的设计初衷就是针对用户一直在场的场景,所以它无法使用刷新令牌。

小结

OAuth 是用来解决用户对软件授权而开发出来的标准化协议,尽管它的功能不止如此。OAuth 2.0 有多种协议类型,其中常用的叫做「授权码许可类型」和「隐式许可类型」。授权码许可类型虽然很常用、很标准,但不适用于浏览器以外的场景。
隐式许可类型虽然安全性不如授权码许可类型,但胜在简单高效。在原生 APP 与单点登录系统中应用广泛。最后,和其他的软件程序一样,构建一个支持 OAuth 2 协议的工程并不难。难的是如何使其健壮、安全、易用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值