我是Justin Richer,日常主要从事顾问工作,虽然假装自己是个安全怪咖,但我并不是科班出身。我的专业背景是协作技术,主要研究如何让人们借助计算机协同工作。即便如此,我也早就开始使用OAuth了。我用先前的OAuth 1.0实现了好几个服务器和客户端,将它们用在我当时开发的协作系统中。正是从那个时候我开始体会到,如果应用架构想要在真实环境中立于不败之地,那么一个优良的、易实施的、易用的安全系统便不可或缺。大约也是在那个时候,我参加了早期的Internet Identity Workshop会议。在会议上大家讨论了下一代OAuth,决定将OAuth 1.0在实际应用中的经验教训作为下一代OAuth的构建基础。在互联网工程任务组(IETF)开始开发OAuth 2.0时,我加入了这个小组并首次深度参与了讨论。几年之后,我们将规范制定了出来。虽然称不上完美,但它表现得很不错,人们对它青睐有加。
我一直留在OAuth工作组中,并担任了Dynamic Registration(RFC 7591和RFC 7592)和Token Introspection(RFC 7662)这两个OAuth扩展规范的编辑。如今,我是OAuth Proof of Possession(PoP)这一套规范的编辑以及其中某些部分的作者,还担任了多个OAuth配置规范(profile)、扩展以及相关协议的技术编辑。我还参与制定了OpenID Connect核心规范,并和我的团队一起实现了广受好评的MITREid Connect,它是基于OAuth和OpenID Connect的服务端与客户端套件。我突然发现,自己在向许多不同的受众谈论OAuth 2.0,并在各种各样的系统上实现它。我教过课,做过演讲,还写过一些关于OAuth 2.0的文章。
所以,当Antonio Sanso—— 一位受人尊敬的安全研究者——邀请我合著本书的时候,我们一拍即合。在搜寻了市面上有哪些关于OAuth 2.0的书之后,我们发现一本喜欢的都没有。我们找到的大部分书都是针对具体服务的,例如,如何写一个能和Facebook或Google交互的OAuth客户端,或者如何使用GitHub API对本地应用授权。如果你所关心的只是这方面的内容,那资料真是相当丰富。但我们没有看到有哪本书向读者介绍整个OAuth系统,解释其工作原理,指出其弱点、局限性以及优势。我们决定要以全方位的视角来写这样一本书,并且力求完美。因此,本书不会涉及任何与现实中特定OAuth服务商的交互,也不会详细讨论特定的API或行业领域。相反,本书的焦点在于OAuth本身的原理,希望让读者看到:当曲柄转动时,每一个齿轮是如何啮合的。
我们构建了一个代码框架,希望读者将注意力集中在OAuth的核心部分,而不要过度地陷入平台实现的细节。毕竟,这不是一本教读者“如何在最新的平台上实现OAuth”的书,而是想以本书“讲解OAuth 2.0的工作原理并让读者能在任何平台上使用它”。所以,我们基于Express.js构建了一个相对简易的Node.js框架。为了尽可能地消除平台特异性,我们大量使用了库。尽管如此,JavaScript还是JavaScript,有些特异之处仍然会不时出现,任何平台都会这样。但我们希望,读者能将本书所用的方法和理论应用到自己所喜爱的语言、平台和架构中去。
回顾历史,我们是如何走到现在的?故事开始于2006年,当时包括Twitter和Ma.Gnolia在内的很多Web服务公司,都有很多应用是互通的,他们希望能让用户将这些应用统一地连接起来。当时,此类连接都是通过在远程服务器上向用户索要凭据并将凭据传送到API上来实现的。然而,为方便登录,这些网站都使用了一项分布式身份认证技术——OpenID。这就导致无法将用户名和密码用于API。
为了解决这个问题,开发人员试图发明一个协议,允许用户将API访问授权出去。新的协议基于多个具有同样思想的专有实现,包括Google的AuthSub以及Yahoo!的BBAuth。在这些实现中,客户端应用只要获得用户的授权并得到一个令牌,就能使用这个令牌访问远程API。这些令牌的发放都包含公共和私有的部分,并且该协议使用了一种新型的(现在看来是脆弱的)加密签名机制,使得它可以在非TLS HTTP连接上使用。他们称这个协议为OAuth 1.0,并将其作为一项Web开放标准发布。它很快就获得响应,出现了多种语言对这一标准的自由实现。这一标准表现优秀,深受开发人员喜爱,甚至一些大型互联网公司也很快弃用了自己的专有机制,起初正是这些专有机制启发了OAuth。
和许多新的安全协议一样,OAuth 1.0在早期就被发现有一个缺陷。为了修复这个会话固化漏洞,OAuth 1.0a诞生了。这个版本后来作为RFC 5849被编入了IETF。在那个时候,OAuth协议的社区开始成长,新的用例被开发和实现。其中一些用例将OAuth用在了其原本并未有意适用的场景,但这些对OAuth的非常规使用也比现有的其他可用方案表现得更好。然而,OAuth 1.0只是一个单体协议,意图以不变应万变,用同一种机制来应对所有场景,在有些场景下使用它是有风险的。
RFC 5849发布后不久,Web Resource Access Protocol(WRAP)就发布了。这份协议采用了OAuth 1.0a的核心——客户端、委托、令牌——并对它们进行了扩展,以适应不同的需求。WRAP废除了OAuth 1.0中很多令人困惑和容易出问题的部分,比如自定义签名算法机制。经过社区的多次讨论,WRAP被确定为新的OAuth 2.0协议的基础。OAuth 1.0是单体的,而OAuth 2.0是模块化的。OAuth 2.0的模块化使其成为了一个框架,能够部署和运用在OAuth 1.0已经实践过的所有场景中,但并不会让协议的核心内容发生扭曲。本质上来说,OAuth 2.0在基础层面提供了设计规范。
2012年,IETF批准了OAuth 2.0核心规范,但是留给社区的工作还远未完成。规范被模块化地分成互补的两个部分:RFC 6749详细说明了如何获取令牌,RFC 6750则详细说明了如何在受保护的资源上使用一种特定类型的令牌(bearer令牌)。另外,RFC 6749的核心部分还详细阐明了多种获取令牌的方式,并提供了一种扩展机制。OAuth 2.0定义了4种许可类型,分别适用于不同的应用类型,而不是单单定义一种复杂的方法来适应不同的部署模型。
如今,OAuth 2.0已经是互联网上首选的授权协议。它被广泛使用,从大型互联网公司到小型创业公司,几乎所有的地方都在使用它。由基于OAuth 2.0构建的扩展、配置规范和完整协议组成的生态系统已经出现,人们也不断探索出对这一基础技术更多新颖、有趣的应用方式。本书的目标是帮助读者不仅理解OAuth 2.0是什么以及它的工作原理,而且还要知道如何以最佳的方式来用它解决问题、构建系统。
Justin Richer