[Authentication] 身份认证生存指南

sddtc 于 2019-05-25 发布

身份认证一直是一个复杂的主题,如果你认为不复杂那么有一点Too young too simple,因为最终你会发现它其实会令人头秃。 这个主题有大量的技术实现方案,各种协议和概念,还有该做什么/不该做什么。最重要的是,我们很难去追踪所有不断被发现的最佳实践,亦或是已经过时的概念和术语。

如果你恰好是一位想要了解身份认证领域相关知识的人,这篇文章会帮你了解一些相关协议,形式,概念或者常用术语; 对于那些正处于实现工作的开发人员,这篇文章可以帮助你了解怎样重新设计身份认证系统,亦或者是解决一些疑惑。

然而,这篇文章并不是一份关于身份认证的权威参考,但可以被当作一份快速入门指南。

基本概念

身份认证 Authentication

当你需要访问一款应用,该应用需要知道你是”谁”, 应用给你一把钥匙使你能进入该应用。 其表现形式便是应用程序拥有与”你”相关的一些属性: 邮箱,手机号码,验证码或者密码等。

鉴权 Authorization

当你访问一款应用,应用不仅需要知道你是”谁”, 应用还需要知道你可以使用钥匙打开哪扇门。 应用拥有各种资源,你可以访问其中部分或者是全部的资源。授权往往依赖于身份认证部分来先确定用户的身份。

身份提供商 Identity provider

创建,维护和管理身份信息的实体。提供与”你”相关的那些属性,访问资源信息。

数字签名 Digital Signature

运用技术手段来保证身份提供者的信息的真实性。 举例来说,你使用正确的邮箱和密码登陆了一款应用,应用程序给你了一把”钥匙”, 当你使用这把钥匙开门的时候, 应用程序需要保证这把钥匙:1.是该款应用制造出来的。2.没有被修改过。

授权框架 - OAuth 2.0

它是一个授权框架,它可以让一款应用拥有访问HTTP资源的能力。 举例来说,你开发了一款应用程序,支持使用Cacebook账号登陆,登陆之后可以正常访问你的应用。在这个例子中,你的应用不需要知道和存储用户的Cacebook的密码,你的应用就是资源服务器;如此一来,你依赖了Cacebook的身份信息,并提供了该身份信息访问应用资源的权限。这一套流程就可以使用OAuth2.0来实现。

OAuth2.0 主要包括以下几部分: Authorization server: 授权服务器 Client application: 客户端 Resource owner: 资源持有者 Resource server: 资源服务器

这里最重要的部分不是身份认证,而是通过身份认证来确保用户是谁,继而给用户授权,该框架/协议最终被用作实现用户身份认证的方式。

举例来说:

  1. 一个用户想要登陆并使用应用X,他有Cacebook的账号,并且应用X支持用户使用Cacebook账号登陆
  2. 用户登陆时,应用X向Cacebook发送一个登陆请求
  3. 用户被导航到一个新页面,该页面由Cacebook提供,允许用户使用Cacebook的账号登陆
  4. 登陆成功之后,Cacebook会给用户展示一个页面,询问用户是否同意应用X访问他在Cacebook的信息数据
  5. 当用户同意授权,那么页面会跳回到应用X
  6. 应用X会接收一些信息(没错,是一个token令牌),用来证明用户拥有一些来自Cacebook的属性集

如你所见,应用X仅仅要求访问Cacebook的资源,身份认证只是必要的中间步骤。他收到的token令牌,或许包含了这个用户是”谁”,或许可以使用它来查询Cacebook的数据。许多应用仅仅利用该授权框架做身份认证:即应用对能够访问的Cacebook的数据资源没有兴趣,只想标识用户。

当下一次用户再次使用Cacebook账户登陆应用X,步骤依然如上所述。

虽然OAuth2.0可以用于身份认证,但您要知道这并不是它的设计目的。它的出现,最常见的适用场景是授予第三方应用程序访问HTTP-REST-APIs的能力。

如果你计划或者已经拥有了一个HTTP API能提供一些数据信息,并且这些信息与用户相关(不是公开的资源,和用户绑定),你还想支持第三方应用程序能够访问你的API,那么,OAuth2.0很可能是最适合你的工具,至少它是一个当前最流行且使用人数最多的框架。对于这种需求,使用OAuth2.0也相当安全可靠。

但请记住,它不是唯一的选择,对于简单的场景几乎可以肯定有更简单的解决方案;例如你的需求是想知道谁在调用你的API,那么使用API keys会直接达到目的。所以根据具体的情况权衡利弊,找到最适合的选择。

另一方面,细心的你应该已经发现,OAuth2.0试图将自己定位为授权框架而不是协议是刻意为之。 虽然它涵盖了实现的最低要求,但它仍要保留许多方面的特殊实现。

这意味着如果你的应用需要利用OAuth2.0来开发,则需要执行以下2个操作:

  1. 了解规范(RFC 6749)如何与服务提供者集成。
  2. 阅读服务提供者文档,了解他们的特殊实现或要求。

如果你站在非客户端应用的角度,而是授权服务器的角度,我最好的建议是尝试使用已经存在的东西。如果你没有这个打算,我并不能够提供明确的资源列表,你可以阅读和了解OAuth2.0的威胁模型和安全注意事项(RFC 6819)-它们可能有助于改变你的想法(或许会参考我的建议)。

身份认证协议 - OpenID Connect

事实证明OAuth2.0非常受欢迎,人们开始将其用于原始意图之外的场景-用户身份验证和识别。然而,当这个框架仅用于解决认证问题时,那么说明读者还需要更多的了解-框架需要更多的被了解。

这导致类似的问题出现在不同的解决方案中,然后在特定的应用中反映出来。缺乏明确的指导方针也意味着应用开发人员更难保证安全性。 因此,OpenID Connect可以被描述为OAuth2.0的扩展,为如何实现功能和安全的身份认证系统提供明确的指导。

通过专注于解决用户身份认证的特定问题,该协议使一些灰色区域(不同的OAuth2.0的实现方式)标准化。例如,在OAuth2.0不对所使用的token令牌施加任何格式和特定要求的情况下,OpenID Connect声明ID令牌使用JWT格式,并且它始终具有一组可用的标准声明。

此外,该协议还通过UserInfo的接口标准化了身份提供者提供的有关用户身份的信息方式。这是许多提供商已经支持的事情,然而,通过公开透明化来统一一个可用于实现的基线非常有意义。

与其他身份验证相关的协议相比,作为一个新生儿,它仍然在不断的发展:在库和身份提供商方面。当你想要寻求新的身份认证的相关规范时,对你来说它会是一个非常不错的选择。

总而言之,为了解决应用中的身份认证问题,它是你需要学习和使用的协议。

令牌 Token overload

令牌就像一把钥匙,让应用和资源服务知道你是”你”。令牌的正确存储是基于令牌的身份认证系统必须具备的要求。

对于移动端会要求你必须使用可确保设备上其他应用无法访问令牌的存储方式。举例来说,在Android中,SharedPreferences特性可以实现该需求。

基于浏览器的应用的情况便有些棘手;你有一些选择作为存储令牌的方法,各有各的优劣。选择基于浏览器的应用的存储方式就像一个挑选毒药的游戏-你的选择取决于你的需求,为此,我们会分析出各个选择的相关优点和缺点。

存储 Storage

Web Storage(local Storage or session Storage)
优点:

  1. 浏览器不会自动保存从Web Storage到HTTP请求的任何信息,使其不易受到CSRF攻击
  2. 只能在创建数据的相同域名中运行JS访问令牌
  3. 允许使用最佳方式来传递令牌(在Authorization header 传递 Bearer token)
  4. 很容易识别包含身份认证的请求

缺点:

  1. 无法跨域访问或者在子域中通过JS访问令牌
  2. 易受到XSS攻击
  3. 为了执行身份认证的请求,你只能使用允许自定义请求的浏览器/APIs

HTTP-only cookie
优点:

  1. 不易受到XSS攻击
  2. 浏览器会自动识别并存储满足cookie规范的请求的令牌(域,路径和生命周期)
  3. 可以在顶级域名创建cookie, 在子域执行的请求中使用

缺点:

  1. 易受到CSRF攻击
  2. 你需要注意并考虑到cookie被子域的请求所使用或滥用
  3. 拿到包含cookie的请求可行但麻烦
  4. 你可能会遇到一些浏览器对于cookie处理方式不同的问题:虽然差异小但有可能遇到
  5. 如果不小心,你可能会因为避免遭受CSRF攻击而带来一些XSS问题
  6. 服务端需要验证cookie来进行身份认证而不是使用更好的Authorization header来处理

Javascript accessible cookie(ignored by server-side)
优点:

  1. 不易受到CSRF攻击(因为他被服务端忽略)
  2. 可以在顶级域名创建cookie, 在子域执行的请求中使用
  3. 允许使用最佳方式来传递令牌(在Authorization header 传递 Bearer token)
  4. 很容易识别包含身份认证的请求

缺点:

  1. 易受到XSS攻击
  2. 如果你不小心设置了不合适的cookie的路径,那么会增加一些不必要的开销
  3. 为了执行身份认证的请求,你只能使用允许自定义请求的浏览器/APIs

这看起来是一个奇怪的选择,但它确实有一个好处,可以让令牌应用于顶级域名和子域名,这是Web Storage不具备的能力,但是实施起来更加复杂。


在大部分应用场景里,我比较推荐使用Web Storage, 原因如下:

  1. 任何一款Web应用都不可避免的易受到XSS攻击,要始终独立的存储令牌
  2. 如果你不选择基于cookie的身份认证存储方式,那么CSRF的问题不会出现在你的应用里

以上没有任何基于cookie的选择提及到HTTPS,但我们默认必须使用HTTPS。

另外,存储令牌不是客户端才有的需求,如果你正在实现或维护着授权服务器你同样需要存储令牌以便确认它的有效性。在这些情况下,你会关注第三方应用创建的令牌。

虽然令牌特别像JWT这样的自包含格式当下风靡万千,但cookie并未过时。从cookie的角度来说这只是一种状态管理机制甚至可以被当作令牌存储在客户端。如果你认为cookie有助于简化你的需求,那么便不要完全拒绝他们,虽然他们不是新的闪亮的东西,但你依然可以使用cookie将会话状态推送到客户端。请记住正确的签名或对他们进行加密。cookie最大的缺点便是由于他们自动包含在用户请求里,因而容易被CSRF攻击。

以上主要介绍了关于身份认证相关的概念,还有关于令牌的存储选择。 在实际工作中,我们通常会利用各个平台提供的身份认证服务来解决个人应用的身份认证需求,在我看来,使用第三方登录可以帮助你减化开发登录系统这整个模块的工作量,继而专注在核心的业务实现。

接下来,我会和大家分享当下主流的网站开放出来的接口和他们提供的服务。