第 1 章 OAuth 2.0是什么,为什么要关心它

第 1 章 OAuth 2.0是什么,为什么要关心它

本章内容

  • OAuth 2.0是什么
  • 如果不用OAuth,开发人员怎么做
  • OAuth的原理
  • OAuth 2.0不能做什么

如果你从事Web软件开发,就应该听说过OAuth。它是一个安全协议,用于保护全球范围内大量且在不断增长的Web API,从Facebook、Google等大型服务商,到创业公司和各类企业内部的小型一次性API。它用于连接不同的网站,还支持原生应用和移动应用与云服务之间的连接。它是各领域标准协议中的安全层,覆盖了从医疗到身份管理,从能源到社交网络的广阔应用领域。OAuth已成为当今Web上占主导地位的安全手段,它的无处不在为开发人员保护其应用铺平了道路。

但是,它是什么,它如何工作,为什么我们需要它?

1.1 OAuth 2.0是什么

OAuth 2.0是一个授权协议,它允许软件应用代表(而不是充当)资源拥有者去访问资源拥有者的资源。应用向资源拥有者请求授权,然后取得令牌(token),并用它来访问资源。这一切都不需要应用去充当资源拥有者的身份,因为令牌明确表示了被授予的访问权。从很多方面来说,你可以把OAuth令牌看作Web上的“泊车钥匙”。不是所有车都有泊车钥匙,但是对于有泊车钥匙的车来说,把泊车钥匙交给泊车员比直接交出常规钥匙更安全。泊车钥匙限制泊车员只能操作点火开关和车门,而不能打开后备箱和手套箱。更高级的泊车钥匙还能限制最高车速,甚至能在车辆行使超过车主设定的距离后强制停车并向车主发出警报。同样的道理,OAuth令牌可以限制客户端只能执行资源拥有者授权的操作。

举个例子,假设你使用了一个照片云存储服务和一个云打印服务,并且想使用云打印服务来打印存放在云存储服务上的照片。很幸运,这两个服务能够使用API来通信。这很好,但两个服务由不同的公司提供,这意味着你在云存储服务上的账户和在云打印服务上的账户没有关联。使用OAuth可以解决这个问题:授权云打印服务访问照片,但并不需要将存储服务上的账户密码交给它。

虽然OAuth基本上不关心它所保护的资源类型,但它确实很适合当今的RESTful Web服务,也适用于Web应用和原生应用。从小型单用户应用,到有数百万用户的互联网API,它都适用。在受控的企业环境中,它能对新一代内部业务API和系统访问进行管理,在它所成长起来的纷乱复杂的Web环境中,它也能游刃有余地保护各种面向用户的API。

而这还不是全部:如果你在过去5年内使用过移动应用或者Web应用,很可能已经使用过OAuth来授权应用。实际上,不管有没有意识到,只要你见过如图1-1所示的页面,那就使用过OAuth。

图 1-1 本书练习框架中的一个OAuth授权页面

在一般情况下,OAuth协议是不会被用户觉察到的,例如Steam和Spotify的桌面应用。除非主动地去探寻OAuth的痕迹,否则用户永远不会知道他使用了OAuth。1这是很好的特性,因为优秀的安全系统在一切功能都正常时就应该不被觉察。

1好消息是读完本书,你可以自己去找出所有的OAuth使用痕迹了。

众所周知,OAuth是一个安全协议,但是它到底有什么用途呢?既然你捧起了一本关于OAuth 2.0的书,那么问这个问题理所当然。协议规范2是这样定义的:

2RFC 6749:https://tools.ietf.org/html/rfc6749

OAuth 2.0框架能让第三方应用以有限的权限访问HTTP服务,可以通过构建资源拥有者与HTTP服务间的许可交互机制,让第三方应用代表资源拥有者访问服务,或者通过授予权限给第三方应用,让其代表自己访问服务。

稍微解释一下:作为一个授权框架,OAuth关注的是如何让一个系统组件获取对另一个系统组件的访问权限。在OAuth的世界中,最常见的情形是客户端应用代表资源拥有者(通常是最终用户)访问受保护资源。到目前为止,我们需要关心如下组件。

  • 资源拥有者有权访问API,并能将API访问权限委托出去。资源拥有者一般是能使用浏览器的人。因此,本书在插图中将资源拥有者表示为一个坐在浏览器前的人。
  • 受保护资源是资源拥有者有权限访问的组件。这样的组件有多种形式,但大多数情况下是某种形式的Web API。虽然“资源”听起来就像是某种能下载的东西,但其实这些API支持读、写和其他操作。本书在插图中将受保护资源表示为带有锁图标的机架式服务器。
  • 客户端是代表资源拥有者访问受保护资源的软件。如果你是Web开发人员,“客户端”这个名称会让你觉得它是指浏览器,但它在本书中并不是这个意思。如果你是商业应用开发人员,可能以为“客户端”是指付费使用服务的客户,3但这也不是它的正确含义。在OAuth中,只要软件使用了受保护资源上的API,它就是客户端。每当本书提到“客户端”一词时,几乎都是特指该含义。本书插图中将客户端绘制成带有齿轮的计算机屏幕。由于实际存在的客户端应用类型有多种(如第6章所述),因此没有一个图标能普遍适用。

3client有“客户”的含义。——译者注

第2章将更深入地探讨这些细节。但现在,你要知道整个系统的目标是:让客户端为资源拥有者访问受保护资源(如图1-2所示)。

图 1-2 代表资源拥有者连接客户端

在照片打印的例子中,假设你将度假时拍的照片上传到了照片存储网站,现在想要将它们打印出来。照片存储网站的API就是资源,打印服务则是那个API的客户端。作为资源拥有者,你需要将一部分权力委托给照片打印服务,让它能读取你的照片。但你可能不想让打印服务读取所有的照片,也不想让它有删除或者上传照片的权限。总之,你的目的就是打印你指定的照片。如果你和大多数用户一样,那么可能并不会去思考:怎样的安全架构才能实现这一目标。

值得庆幸的是,由于你正在阅读本书,因此应该和大多数用户不一样,会很关注安全架构。下一节会展示在不使用OAuth的情况下,如何以不那么完美的方式解决这个问题,然后再展示如何使用OAuth更好地解决这个问题。

1.2 黑暗的旧时代:凭据共享与凭据盗用

连接多个不同的服务并不是什么新鲜事,而且毫无疑问,从世上出现网络互联的服务开始,这种情况就存在了。

企业流行的做法是,复制用户的凭据并用它登录另一个服务(如图1-3所示)。在这种情况下,照片打印服务要假定用户在照片存储服务上使用的凭据与在照片打印服务上的相同。当用户登录照片打印服务后,该服务使用用户的用户名和密码登录照片存储网站,获取用户的账户访问权,假装用户。

图 1-3 不征求同意就复制资源拥有者的凭据

在这种情况下,用户需要使用某种凭据与客户端进行身份认证,这些凭据通常是被集中控制的,并受客户端和受保护资源一致认可。客户端先得到用户的用户名和密码或者会话cookie,然后用它们访问受保护资源,假装是用户。受保护资源将客户端视为用户并直接通过身份认证,而实际上与受保护资源建立连接的是客户端,正如前面所要求的那样。

这种方法要求用户在客户端和受保护资源端使用相同的凭据,使得这种凭据盗用技术只能在同一安全域内使用。也就是说,如果是一个公司控制着客户端、授权服务器和受保护资源,并且这些组件都在相同的策略和网络控制下运行,这种方法才行得通。如果打印服务和存储服务是由同一个公司提供的,就能采用这种方法,因为用户可以在两个服务上使用相同的账户凭据。

这一技术还会将用户的密码暴露给客户端应用,即使在单一安全域中使用同一组凭据,这也基本上无法避免。但无论如何,客户端是在扮演用户,受保护资源无法区分资源拥有者和扮演资源拥有者的客户端,因为二者都以同样的方式使用相同的用户名和密码。

但是,如果两个服务位于不同的安全域中,如照片打印例子中的情况,又会怎样呢?不能再复制用户提供的用于登录当前应用的密码了,因为这个密码对于另一个应用来说是无效的。对于这个问题,可以采取一种老套的手段来获取密码:向用户索要(如图1-4所示)。

图 1-4 向资源拥有者索要凭据并用于访问受保护资源

如果打印服务想要获取用户的照片,它可以提示用户输入其照片存储网站上的用户名和密码。然后就像前面那样,打印服务用这些凭据访问受保护资源,扮演用户。在这种情况下,用户用于登录客户端的凭据和用于访问受保护资源的凭据可以不同。不管怎么说,客户端通过向用户索要用于访问受保护资源的用户名和密码,解决了这个问题。很多用户在实际中会允许这样的要求,特别是当使用受保护资源的是一个很有用的服务时。因此,这仍然是当前移动应用通过用户账户访问后端服务的最常用方法之一:移动应用让用户输入用户名和密码,然后直接将这些凭据通过网络发送给后端API。为了可以持续访问API,客户端应用会保存用户的凭据,以便在必要的时候用于访问受保护资源。这种做法极其危险,因为一旦任何一个正在使用中的客户端被攻破,就意味着该用户在所有系统中的账户都被攻破。

在极少数场景下,这种方法还是可行的:客户端需要直接获得用户的凭据,并且能在用户不在场的情况下将这些凭据用于服务。这排除了多种用户登录方式,包括几乎所有联合登录系统、很多多因素身份认证登录系统,以及大多数高安全等级的登录系统。

LDAP身份认证

有趣的是,这恰恰就是LDAP(lightweight directory access protocol,轻型目录访问协议)这样的密码身份认证技术使用的模式。在使用LDAP进行身份认证的时候,客户端应用直接从用户那里获取凭据,然后通过LDAP服务器检验它们是否有效。客户端系统在这个处理过程中必须得到用户的明文密码,否则无法向LDAP服务器验证密码的正确性。从本质上来说,这就是针对用户的中间人攻击,虽然通常是善意的。

只要采用这种方法,就会将用户最重要的凭据暴露给可能并不可信的应用——客户端。为了能一直充当用户,客户端就不得不以一种可重现的形式(通常是明文或者某种可逆的加密机制)存储用户的密码,用于后续访问受保护资源。如果客户端应用被攻破,攻击者不仅能访问客户端,还能访问受保护资源以及用户使用的其他具有相同密码的服务。

而且,在以上的这些方法中,客户端应用充当资源拥有者,受保护资源无法分辨某个调用是由资源拥有者直接发起的,还是由客户端代发的。这有何不妥呢?再回去看看打印服务的例子。在少数情况下,大多数方法都可行,但考虑一下这种情形:你不希望打印服务能向存储服务中上传照片或删除其中的照片,而只能读取你要打印的照片,还希望它只能在需要打印的时候读取照片,并能随时解除其访问权限。

如果打印服务需要以你的身份访问照片,存储服务将无法辨别请求的发起者是你还是打印服务。如果打印服务在背地里偷偷将你的密码保存下来(虽然它保证过不会这样做),那它就可以随时冒充你并窃取你的照片。阻止这一流氓行为的唯一方法就是修改密码,让打印服务之前保存的密码失效。更糟糕的是,很多用户都喜欢在不同系统中使用相同的密码,这可能导致所有关联账户都受到牵连。坦率地说,为了解决多个服务连接的问题,我们引发了更严重的问题。

现在你已经看到,复制用户密码并不是一个好方法。如果授予打印服务全局的访问权限,使它能代表由它指定的任何用户并访问存储服务上的所有照片,又会是怎样的情况呢?常用的方式是为客户端颁发一个开发者密钥(如图1-5所示),让客户端使用该密钥直接调用受保护资源。

图 1-5 使用全局的开发者密钥,确定你宣称代表的用户

在这种方法中,开发者密钥是一种全局的密钥,客户端可以用它来充当任意一个由其指定的用户,用户的指定很可能通过一个API参数来完成。这样做的好处是避免了向客户端暴露用户凭据,但代价是要向客户端提供功能强大的开发者密钥。有了这种密钥,打印服务随时都能任意地打印所有用户的所有照片,因为它实际上拥有了自由访问受保护资源的权力。这在一定程度上是可行的,但前提是受保护资源要充分了解并信任客户端。但是这样的关系几乎不可能存在于两个组织之间,例如照片打印例子中的两个服务。此外,如果客户端的密钥被盗,将对受保护资源造成灾难性的损害,因为存储服务的所有用户都会受到影响,无论他们是否使用打印服务。

还有一个方法是给用户一个特殊的密码(如图1-6所示),此密码仅用于透露给第三方服务。用户自己不会使用这个密码来登录,只是将它粘贴到所使用的第三方应用里。这听起来很像本章最开始提到的那种功能有限的泊车钥匙。

图 1-6 针对具体服务且访问受限的密码

现在,距离理想的系统又近了一步,因为用户不再需要向客户端透露登录密码,受保护资源也不再需要相信客户端时刻都能代表所有用户执行正确的操作。但是,这种系统的可用性并不好。它要求用户除了管理自己的主密码之外,还要创建、分发和管理特殊的凭据。因为需要用户来管理这些凭据,所以一般来说,客户端与凭据本身并没有对应关系。这使得撤销某个具体应用的访问权限变得很困难。

还有更好的办法吗?

如果能为每个客户端和每个用户的组合分别颁发这种对受保护资源具有受限访问权限的凭据,会怎样?如此一来,就可以将受限访问权限分别与这些受限的凭据绑定。更进一步,如果有一个基于网络的协议,能够部署到整个互联网上,跨安全边界地生成并安全分发这些受限的凭据,同时具有良好的用户体验,又会怎样?接下来就开始讨论这一有趣的话题了。

1.3 授权访问

OAuth协议的设计目的是:让最终用户通过OAuth将他们在受保护资源上的部分权限委托给客户端应用,使客户端应用代表他们执行操作。为实现这一点,OAuth在系统中引入了另外一个组件:授权服务器(如图1-7所示)。

图 1-7 OAuth授权服务器自动发送服务专用的密码

受保护资源依赖授权服务器向客户端颁发专用的安全凭据——OAuth访问令牌。为了获取令牌,客户端首先将资源拥有者引导至授权服务器,请求资源拥有者为其授权。授权服务器先对资源拥有者进行身份认证,然后一般会让资源拥有者选择是否对客户端授权。客户端可以请求授权功能或权限范围的子集,该子集可能会被资源拥有者进一步缩小。一旦授权请求被许可,客户端就可以向授权服务器请求访问令牌。按照资源拥有者的许可,客户端可以使用该令牌对受保护资源上的API进行访问(如图1-8所示)。

图 1-8 完整的OAuth工作过程

在这个过程中,没有将资源拥有者的凭据暴露给客户端:资源拥有者向授权服务器进行身份认证的过程中所用的信息是独立于客户端交互的。客户端没有功能强大的开发者密钥,无法随意访问任何资源,而是必须在得到有效的资源拥有者授权之后才能访问受保护资源。虽然大多数OAuth客户端可以向授权服务器进行身份认证,但仍然需要得到授权后才能访问资源。

用户通常不必查看或者直接处理访问令牌。OAuth不需要由用户生成令牌并粘贴到客户端,而是简化了这一过程:客户端请求令牌,用户对客户端授权。然后由客户端管理令牌,用户管理客户端应用。

以上是对OAuth工作原理的一般性概述,但实际上OAuth拥有多种获取访问令牌的方法。第2章将介绍OAuth 2.0的授权码许可类型,详细讨论其工作过程。其他获取令牌的方法将在第6章介绍。

1.3.1 超越HTTP基本认证协议和密码共享反模式

上一节中列出的许多“传统”方法都是密码反模式的案例,它们通过共享机密信息(密码)来直接代表当事方(用户)。用户通过与应用共享密码,使应用能够访问受保护的API。然而,正如我们已经揭示的那样,这种做法在现实中存在很多问题。密码本身可能被盗或者被猜到;同一个用户可能在不同的服务上使用完全相同的密码;为了以后能继续访问API而保存密码会使得密码更易被盗。

HTTP API最开始是如何引入密码保护功能的呢?我们可以从HTTP协议的历史及其安全手段看出端倪。HTTP协议制定了一个机制,用户可以凭借该机制在浏览器中使用用户名和密码向一个网页进行身份认证,这就是所谓的HTTP基本认证协议(HTTP basic auth)。还有一种更安全的认证协议,叫作HTTP摘要认证(HTTP digest auth)。但是就我们的目的来说,它们没有什么区别,因为它们都假设用户在场,并且要求向HTTP服务器呈递用户的用户名和密码。此外,由于HTTP是一个无状态的协议,因此每一个HTTP事务都要呈递这些凭据。

鉴于HTTP原本是一个文档访问协议,这一切都是合理的。但是Web的规模和应用范围自那以后已显著扩大。作为一个协议,HTTP不会区分一个事务是由用户通过浏览器发起的,还是通过其他软件发起的。这种基本的灵活性是HTTP协议得到普及的关键原因。但结果是,除了面向用户的(网页)服务之外,当HTTP开始被用于直接访问API时,其现有的安全机制顺理成章地被沿用到新的应用场景中。这个不明智的技术决策导致了一种长期存在的错误做法:为API和网页服务不断地呈递密码。虽然浏览器可以使用cookie或者其他会话管理技术,但是访问Web API的HTTP客户端没有这样的机制可用。

OAuth从一开始就被设计成一个用于API的协议,其中主要的交互过程都是在浏览器之外进行的。OAuth的整个流程通常是由最终用户在浏览器中启动的,实际上这也正是委托模式的灵活性和优势所在。但是最终接收令牌、使用令牌访问受保护资源的步骤对用户是不可见的。实际上,OAuth的一些主要事务过程都发生在用户不在场的情况下,客户端仍然能够代表用户执行操作。OAuth让我们摒弃HTTP基本协议中的观念和假设,将一种功能强大、安全的方式引入现今的API体系。

1.3.2 授权委托:重要性及应用

委托概念是OAuth强大功能的根基。虽然OAuth经常被称作授权协议(这是RFC中给出的名称),但它也是一个委托协议。通常,被委托的是用户权限的子集,但是OAuth本身并不承载或者传递权限。相反,它提供了一种方法,让客户端可以请求用户将部分权限委托给自己。然后,用户可以批准这个委托请求。被批准之后,客户端就可以去执行那些操作了。

以照片打印为例,照片打印服务可以询问用户:“你是否在这个存储服务上存放了照片?如果是,我可以帮你将它们打印出来。”然后用户被引导至照片存储服务,存储服务也会询问:“打印服务想要获取你的照片,你同意吗?”用户可以决定是否同意,即决定是否将访问权限委托给打印服务。

在这里,委托协议和授权协议的区别是很重要的,因为OAuth令牌中携带的授权信息对系统中的大部分组件是不透明的。只有受保护资源需要了解授权信息,只要它能从令牌中得知授权信息(既可以直接从令牌中获取,也可以通过某种服务来获取),它就可以按要求提供API服务。

连接到网络世界

OAuth中的许多概念并不新颖,甚至是从先前的安全体系中借鉴而来的。然而,OAuth 2.0是一个为Web API世界而生的协议,访问这些API的是客户端软件。OAuth 2.0框架提供了一系列用于连接这些应用软件和API的工具,适用于各式各样的场景。在后面的章节中,你将会看到,同样的核心概念和协议可用于连接网页应用、Web服务、原生和移动应用,甚至物联网中的小型设备(使用扩展协议)。纵观这一切,OAuth依赖一个相互连接的网络世界,并使得在此基础上构建新生事物成为可能。

1.3.3 用户主导的安全与用户的选择

由于OAuth的委托过程需要资源拥有者的参与,因此它提供了一种在很多其他安全模型中不存在的可能性:重要的安全决策可以由最终用户来做。传统上,安全决策一直由集权机构负责。由集权机构决定谁可以使用服务、使用什么客户端以及以何种目的使用。OAuth则允许集权机构将某些决策权交到最终使用软件的用户手中。

OAuth系统常遵循TOFU原则:首次使用时信任(trust on first use)。在TOFU模型中,需要用户在第一次运行时进行安全决策,而且并不为安全决策预设任何先决条件或者配置,仅提示用户做出决策。这个过程可以简单到只是询问用户“要连接到新的应用吗”。当然,很多实现允许在这个步骤中进行更多控制。无论用户遇到的是哪种情况,只要具有相应的权限,他们就能做出安全决策。系统会记住用户的决策,以便以后使用。换句话说,只要首次建立了授权关系,系统就会在后续的处理过程中继续信任用户的决策:首次使用时信任。

TOFU是强制要求吗?

OAuth实现并不强制要求采用TOFU方法管理安全决策,但是这两种技术经常结合使用。这是为什么呢?因为要求用户在一个上下文环境中做出安全决策具有很强的灵活性,而不断地要求用户做决策会让人疲倦,TOFU方法在这两者间实现了良好的平衡。如果从TOFU中去掉“信任”的部分,委托就无从谈起。如果去掉“首次使用”的部分,则用户将会很快因无休止的访问请求变得麻木。这种由安全系统造成的疲劳感会引起工作懈怠,这比安全系统原本要解决的安全问题更危险。

这种方法还可以让用户从功能而不是安全性的角度做出决策:“是否允许此客户端执行它请求的操作?”这是与传统安全模型的一个重要区别。在传统安全模型中,决策者需要提前限定哪些行为是不允许的。而这样的安全决策通常会令普通用户不知所措。无论如何,用户更关心的是他们想要完成哪些事情,而不是试图阻止哪些事情。

但这并不是说TOFU必须用于所有的事务或决策。实际上,安全架构师可以采用3层名单机制(如图1-9所示),它具有很强的灵活性。

由白名单确定已知的良好和受信任的应用,由黑名单确定已知的不良应用或者其他糟糕的参与者。这些决策很容易根据系统策略做出,而不需要最终用户参与。

在传统的安全模型中,讨论到这里就结束了,因为任何不在白名单里的内容会默认自动归入黑名单。然而,如果用上TOFU方法,就可以在上述的两个名单中间增加一个灰名单,在这个名单中,会优先考虑用户在运行时做出的信任决策。会有一定的策略来记录和审查这些用户决策,以使风险最小化。通过灰名单功能,系统的可扩展性得到了极大提升,同时又不牺牲安全性。

图 1-9 平行的信任分级

1.4 OAuth 2.0:优点、缺点和丑陋的方面

OAuth 2.0非常善于获取用户的委托决策,并通过网络传递出去。它允许多方参与安全决策过程,尤其是在运行期间让最终用户参与决策。它是由多个可移动的组件构成的协议,但是在很多方面它都比其他方案更简单、更安全。

OAuth 2.0的设计中有一个重要的假设,就是不受控的客户端总是比授权服务器或者受保护资源多出好几个数量级(如图1-10所示)。这是合理的,因为单个授权服务器可以很轻松地保护多个资源服务器,并且很可能有许多不同类型的客户端想要访问特定API。一台授权服务器甚至可以有多个不同的客户端信任等级,第12章将对此进行更深入的讨论。这样的架构决策导致的结果就是,尽可能将复杂性从客户端转移到服务端。这对于客户端开发人员来说是好事,因为客户端成了系统中最简单的部分。客户端开发人员不再需要像在先前的安全协议中那样,处理签名规范化以及解析复杂的安全策略文档,也不需要担心处理敏感的用户凭据。OAuth令牌提供了一种比密码略复杂的机制,但如果使用得当,其安全性要比密码高很多。

图 1-10 OAuth生态系统中各组件的相对数量

另一方面,授权服务器和受保护资源要承担更多复杂性和安全性方面的责任。客户端只要保护好自身的客户端凭据和用户的令牌即可,单个客户端被攻破会造成损害,但只有该客户端的用户会受到影响。被攻破的客户端也不会泄露资源拥有者的凭据,因为客户端根本没有机会接触这些凭据。然而,授权服务器则需要管理和保护系统中所有客户端和用户的凭据和令牌。虽然这确实使它更容易成为攻击目标,但是保护单个授权服务器要比保护上千台由不同开发人员开发的客户端容易得多。

OAuth 2.0的可扩展性和模块化是其最大的优势之一,因为这使得该协议适用于各种环境。然而,正是这种灵活性导致不同的实现之间存在基本的兼容性问题。当开发人员想在不同的系统上实现OAuth时,它提供的众多自定义选项容易使人困惑。

更糟糕的是,OAuth的某些自定义选项可能会被用在错误的地方或者实施不当,进而导致不安全的实现。这些漏洞在OAuth威胁模型文档4以及本书讲述漏洞的部分(第7~10章)有详细讨论。可以说,即使一个系统按照规范正确地实现了OAuth,也不意味着该系统在实践中就是安全的。

4RFC 6819:https://tools.ietf.org/html/rfc6819

总的说来,OAuth 2.0是一个很好的协议,但远远称不上完美。就像所有的技术一样,OAuth 2.0也会在未来某个时候迎来它的继任者,但是在写作本书的时候真正的继任者还没有出现。现在看来,OAuth 2.0的继任者很可能是它自身的配置协议或者扩展协议。

1.5 OAuth 2.0不能做什么

OAuth被许多不同类型的API和应用使用,以前所未有的方式连接网络世界。即使已经无处不在,但OAuth并不是无所不能,明确它的能力范围对理解协议本身很重要。

由于OAuth被定义为一个框架,对于OAuth是什么和不是什么,一直未明确。鉴于此处的讨论目的以及本书的目的,我们所说的OAuth是指OAuth核心规范中定义的协议,5核心规范详述了一系列获取访问令牌的方法;还包括其伴随规范中定义的bearer令牌,6该规范规定了这种令牌的用法。获取令牌和使用令牌这两个环节是OAuth的基本要素。正如我们将在本节中看到的,在更广泛的OAuth生态系统中存在很多其他技术,它们配合OAuth核心,提供更多OAuth本身不能提供的功能。我们认为,这样的生态系统是协议健康发展的体现,但是不应与协议本身混为一谈。

5RFC 6749:https://tools.ietf.org/html/rfc6749

6RFC 6750:https://tools.ietf.org/html/rfc6750

OAuth没有定义HTTP协议之外的情形。由于使用bearer令牌的OAuth 2.0并不提供消息签名,因此不应该脱离HTTPS(TLS上的HTTP)使用。机密信息需要在网络上传播,所以OAuth需要TLS这样的传输机制来保护这些信息。有一个标准定义了如何在简单认证与安全层(SASL)之上使用OAuth令牌。7也有在受限应用协议(CoAP)之上使用OAuth的新尝试。8未来还可能出现使OAuth的部分处理过程运行在非TLS链接之上的尝试(见第15章的讨论)。但即便如此,在使用其他协议和系统时,也需要有一个明确的机制来承担HTTPS事务所承担的任务。

7RFC 7628:https://tools.ietf.org/html/rfc7628

8https://tools.ietf.org/html/draft-ietf-ace-oauth-authz

OAuth不是身份认证协议,虽然可以用它构建一个。正如我们将在第13章深入讨论的那样,尽管用户确实存在,但OAuth事务本身并不透露关于用户的信息。想一想照片打印的例子:照片打印服务不需要知道用户是谁,只需要有人告诉它可以下载照片即可。OAuth本质上只是一个部件,能用于在更宏大的技术方案中提供其他功能。另外,OAuth在多个地方用到了身份认证,最典型的就是资源拥有者和客户端软件要向授权服务器进行身份认证。但这种内嵌身份认证的行为并不会使OAuth自身成为身份认证协议。

OAuth没有定义用户对用户的授权机制,尽管它在根本上是一个用户向软件授权的协议。OAuth假设资源拥有者能够控制客户端。要使资源拥有者向另一个用户授权,仅使用OAuth是不行的。但这种授权并不罕见,User Managed Access协议(将在第14章中讨论)就是为此而生,它规定了如何使用OAuth构建一个支持用户对用户授权的系统。

OAuth没有定义授权处理机制,OAuth提供了一种方法来传达授权委托已发生这一事实,但是它并不定义授权的内容。相反,由服务API定义使用权限范围、令牌之类的OAuth组件来定义一个给定的令牌适用于哪些操作。

OAuth没有定义令牌格式。实际上,OAuth协议明确声明了令牌的内容对客户端是完全不透明的。这不同于之前的一些安全协议,如WS-*、安全断言标记语言(SAML)和Kerberos,这些协议都要求客户端应用能够解析并处理令牌。但是,颁发令牌的授权服务器和接收令牌的受保护资源仍然需要理解令牌。这个层面的互操作性要求催生了JSON Web Token (JWT)格式和令牌内省协议,这将在第11章讨论。虽然令牌本身对客户端还是不透明的,但现在它的格式能被其他组件理解。

OAuth 2.0没有定义加密方法,这与OAuth 1.0不同。OAuth 2.0没有定义新的加密机制,而是允许借用通用的加密机制,这些加密机制不止适用于OAuth。这种有意的遗漏催生了JSON对象签名和加密(JOSE)规范套件,该套件提供了一系列通用的加密机制,可以配合OAuth使用,也可以脱离OAuth使用。第11章将详细说明JOSE规范,第15章会将它用于一种消息级的加密协议,该协议使用了OAuth PoP令牌。

OAuth 2.0不是单体协议。如前所述,该规范被分成了多个定义和流程,每个定义和流程都有各自适用的场景。在某种程度上,可以将OAuth 2.0视为一个安全协议生成器,因为它可用于为许多不同的应用场景设计安全架构。前一节中讨论过,这些系统并不一定要相互兼容。

不同OAuth流程之间的代码复用

虽然OAuth的应用种类繁多,但在迥异的应用之间能够复用大量的代码,而谨慎地应用OAuth协议还可以促进其未来的发展并增强灵活性。例如,假设有两个后端系统需要安全地交互,但不涉及特定用户,比如进行批量数据传输。用传统的开发者密钥就能完成这个任务,因为客户端和资源都在同一个受信任的安全域中。但是,如果系统使用OAuth客户端凭据许可(在第6章讨论),那么系统可以限制令牌的生命周期和访问权限,开发人员则可以在客户端和受保护资源端使用现有的OAuth库和框架,而不用去搞一套完全自定义的东西。由于受保护资源能处理受OAuth令牌保护的请求,因此当未来某个时刻受保护资源希望以每个用户授权的方式提供数据服务时,它就可以很容易地同时支持这两种访问方式。例如,可以为批量传输的数据和用户专用的数据设置不同的权限范围,这样只需对代码稍做改动,受保护资源就可以轻松区分这两种调用。

OAuth无意用一个大而全的协议去解决安全系统所有方面的问题,而是只专注于一件事情,把剩下的问题留给其他组件,让它们各专所长。虽然还有很多议题不在OAuth范围之内,但它提供了一个坚实的基础,可以基于它构建其他更具针对性的工具,从而使安全架构设计更加完善。

1.6 小结

OAuth是一个应用广泛的安全标准,它提供了一种安全访问受保护资源的方式,特别适用于Web API。

  • OAuth关注的是如何获取令牌如何使用令牌
  • OAuth是一个委托协议,提供跨系统授权的方案。
  • OAuth用可用性和安全性更高的委托协议取代了密码共享反模式。
  • OAuth专注于很好地解决小问题集,因而是整个安全系统中一颗很合用的螺丝钉。

接下来要学习OAuth是如何做到这一切的,你准备好了吗?请继续阅读下一章。

目录

  • 版权声明
  • 前言
  • 致谢
  • 关于本书
  • 关于封面图片
  • 第一部分 起步
  • 第 1 章 OAuth 2.0是什么,为什么要关心它
  • 第 2 章 OAuth之舞
  • 第二部分 构建OAuth环境
  • 第 3 章 构建简单的OAuth客户端
  • 第 4 章 构建简单的OAuth受保护资源
  • 第 5 章 构建简单的OAuth授权服务器
  • 第 6 章 现实世界中的OAuth 2.0
  • 第三部分 OAuth 2.0 的实现与漏洞
  • 第 7 章 常见的客户端漏洞
  • 第 8 章 常见的受保护资源漏洞
  • 第 9 章 常见的授权服务器漏洞
  • 第 10 章 常见的OAuth令牌漏洞
  • 第四部分 更进一步
  • 第 11 章 OAuth令牌
  • 第 12 章 动态客户端注册
  • 第 13 章 将OAuth 2.0用于用户身份认证
  • 第 14 章 使用OAuth 2.0的协议和配置规范
  • 第 15 章 bearer令牌以外的选择
  • 第 16 章 归纳总结
  • 附录 A 代码框架介绍
  • 附录 B 补充代码清单