ID令牌和访问令牌的区别

525 阅读10分钟

"让我们使用一个令牌来保护这个API调用。我应该使用ID令牌还是访问令牌?🤔对我来说,ID令牌看起来更漂亮。毕竟,如果我知道用户是谁,我可以做出更好的授权决定,对吗?"

你是否曾发现自己在做类似的争论?基于你的直觉的选择可能听起来不错,但看起来很直观的东西并不总是正确的。就ID和访问令牌而言,它们有清晰明确的目的,所以你应该基于此来使用它们。使用错误的令牌会导致你的解决方案不安全。

"毕竟有什么变化?它们只是代币。我可以按照我认为合适的方式使用它们。可能发生的最坏情况是什么?"

让我们仔细看看这两种类型的令牌,以更好地了解它们在认证和授权过程中的作用。

如果你愿意,你也可以观看这个关于同一主题的视频。

什么是ID令牌?

ID令牌是一个证明用户已被认证的人工制品。它是由OpenID Connect(OIDC)引入的,这是一个开放的认证标准,被许多身份供应商使用,如谷歌、Facebook,当然还有Auth0。请查看这份文件,了解关于OpenID Connect的更多细节。让我们快速看一下OIDC想要解决的问题。

请看下图。

ID token scenario

在这里,一个用户用他们的浏览器对一个OpenID提供者进行认证,并获得对一个网络应用的访问。基于OpenID连接的认证过程的结果是ID令牌,它被传递给应用程序作为用户被认证的证明。

这提供了一个关于ID令牌是什么的非常基本的概念:用户认证的证明。让我们看看其他一些细节。

ID令牌被编码为JSON网络令牌(JWT),这是一种标准格式,允许你的应用程序轻松检查其内容,并确保它来自预期的发行者,没有其他人改变它。如果你想了解更多关于JWT的信息,请查看《JWT手册》

简单地说,一个ID令牌的例子看起来像这样。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vbXktZG9tYWluLmF1dGgwLmNvbSIsInN1YiI6ImF1dGgwfDEyMzQ1NiIsImF1ZCI6IjEyMzRhYmNkZWYiLCJleHAiOjEzMTEyODE5NzAsImlhdCI6MTMxMTI4MDk3MCwibmFtZSI6IkphbmUgRG9lIiwiZ2l2ZW5fbmFtZSI6IkphbmUiLCJmYW1pbHlfbmFtZSI6IkRvZSJ9.bql-jxlG9B_bielkqOnjTY9Di9FillFb6IMQINXoYsw

当然,这对人眼来说是不可读的,所以你必须对其进行解码,以查看JWT拥有哪些内容。 顺便说一下,ID令牌并没有被加密,只是进行了Base 64编码。你可以使用许多可用的库中的一个来解码它,或者你可以用jwt.io调试器自己检查它。

在不深入了解细节的情况下,上面的ID令牌所携带的相关信息看起来如下。

{ 
  "iss": "http://my-domain.auth0.com", 
  "sub": "auth0|123456", 
  "aud": "1234abcdef", 
  "exp": 1311281970, 
  "iat": 1311280970, 
  "name": "Jane Doe", 
  "given_name": "Jane", 
  "family_name": "Doe"
}

这些JSON属性被称为索赔,它们是关于用户和令牌本身的声明。关于用户的声明定义了用户的身份。

实际上,OpenID Connect规范并不要求ID令牌有用户的声明。在其最小的结构中,它没有关于用户的数据;只有关于认证操作的信息。

一个重要的声称是aud 声称。这个要求定义了令牌的受众,也就是要成为令牌最终接收者的网络应用。在ID令牌的情况下,它的值是应该消费该令牌的应用程序的客户端ID。

记住这个关于受众声称的小细节,因为它将帮助你更好地理解它在后面的正确使用。

ID令牌可能有关于用户的额外信息,例如他们的电子邮件地址、照片、生日等等。

最后,也许是最重要的一点:ID令牌是由发行者用其私钥签署的。这可以保证你的令牌的来源,并确保它没有被篡改过。你可以通过使用发行者的公钥来验证这些东西。

爽啊!现在你知道什么是ID令牌了。但你能用ID令牌做什么呢?

首先,它表明用户已经被你信任的实体(OpenID提供者)认证,所以你可以信任关于他们身份的声明

另外,你的应用程序可以通过使用ID令牌中包含的关于用户的声明来个性化用户的体验。例如,你可以在用户界面上显示他们的名字,或者在他们的生日时显示 "祝愿 "信息。有趣的是,你不需要进行额外的请求,所以你的应用程序的性能可能会有一点提高

什么是访问令牌?

现在你知道什么是ID令牌了,让我们试着理解什么是访问令牌。

让我们先来描述一下访问令牌适合的场景。

Access token scenario

在上图中,一个客户端应用程序想要访问一个资源,例如,一个API或任何其他受保护的资源,以防止未经授权的访问。该图中的另外两个元素是用户,即资源的所有者,和授权服务器。在这种情况下,访问令牌是允许客户端应用程序访问用户资源的神器。它是由授权服务器在成功认证用户并获得其同意后发出的。

OAuth 2的背景下,访问令牌允许客户端应用程序访问特定的资源,代表用户执行特定的操作。这就是所谓的委托授权的情况:用户委托客户端应用程序代表他们访问资源。例如,这意味着你可以授权你的LinkedIn应用代表你访问Twitter的API,在两个社交平台上交叉发布信息。请记住,你只授权LinkedIn在Twitter上发布你的帖子。你并没有授权它删除这些帖子或改变你的个人资料数据或做其他事情。这种限制在委托授权的情况下非常重要,是通过作用域来实现的。作用域是一种机制,允许用户授权第三方应用程序只执行特定的操作

当然,收到访问令牌的API必须确定它实际上是由它信任的授权服务器发出的有效令牌,并根据与之相关的信息做出授权决定。换句话说,API需要以某种方式使用该令牌,以便授权客户端应用程序对资源执行所需的操作。

如何使用访问令牌以做出授权决定取决于许多因素:整体系统架构、令牌格式等。例如,访问令牌可以是一个允许API从与授权服务器共享的数据库中检索所需信息的密钥,或者它可以直接包含所需信息的编码格式。这意味着,了解如何检索所需信息以做出授权决定是授权服务器和资源服务器(即API)之间的协议

OAuth 2的核心规范没有提到访问令牌的格式。它可以是任何格式的字符串。访问令牌常用的格式是JWT,并且有一个标准的结构。然而,这并不意味着访问令牌应该采用这种格式。

好了!现在你知道什么是ID令牌和访问令牌了。所以,你已经准备好使用它们而不用担心犯错了。但是,等等。我没有看到你被说服。也许你需要一些其他的信息。好吧。那么,让我们看看这些代币适合做什么。

ID令牌不适合做什么?

开发人员使用ID令牌最常见的错误之一是用它来调用API。

如上所述,ID令牌证明了用户已被认证。在第一方场景中,即在客户端和API都由你控制的场景中,你可能会决定你的ID令牌很适合做授权决定:也许你需要知道的只是用户身份。

然而,即使在这种情况下,你的应用程序的安全性,包括客户端和API,可能会有风险。事实上,没有任何机制可以将ID令牌与客户端-API通道联系起来。如果攻击者设法窃取你的ID令牌,他们可以像合法客户一样使用它来调用你的API。

另一方面,对于访问令牌,有一套技术,统称为发件人约束,允许你将访问令牌绑定到一个特定的发件人。这保证了即使攻击者窃取了访问令牌,他们也不能用它来访问你的API,因为令牌被绑定在最初请求它的客户端。

在委托授权的情况下,第三方客户想要调用你的API,你一定不能使用ID令牌来调用API。除了缺乏将其绑定到客户端的机制外,还有其他几个原因不能这样做。

如果你的API接受ID令牌作为授权令牌,首先,你就忽略了受众声明中的预期接收者。该声明说,它是为你的客户应用准备的,而不是为资源服务器(即,API)准备的。

你可能认为这只是一种形式,但这里有安全问题

首先,在其他验证检查中,你的API不应该接受一个不适合它的令牌。如果它接受了,其安全性就会受到威胁。事实上,如果你的API不关心一个令牌是否是为它准备的,那么从任何客户端应用程序窃取的ID令牌就可以用来访问你的API。当然,检查受众只是你的API应该做的检查之一,以防止未经授权的访问

此外,你的ID令牌不会有授予的作用域(我知道,这是另一个痛点)。如前所述,作用域允许用户限制你的客户应用程序可以代表他们进行的操作。这些作用域与访问令牌相关联,这样你的API就知道客户应用可以做什么,不能做什么。如果你的客户应用使用ID令牌来调用API,你就会忽略这个功能,并有可能让应用执行用户没有授权的操作。

访问令牌不适合做什么?

在访问令牌方面,它被设想用来证明你被授权访问一个资源,例如,调用一个API。

你的客户应用程序应该只为这个原因使用它。换句话说,访问令牌不应该被客户端应用程序检查。它是为资源服务器准备的,你的客户端应用程序应该把访问令牌当作不透明的字符串,也就是说,没有特定含义的字符串。即使你知道访问令牌的格式,你也不应该试图在你的客户端应用程序中解释其内容。如前所述,访问令牌格式是授权服务器和资源服务器之间的协议,客户端应用程序不应侵入。想一想,如果有一天访问令牌的格式改变了,会发生什么?如果你的客户端代码正在检查该访问令牌,现在它将会意外地中断。

快速回顾

对ID和访问令牌的使用的困惑是非常普遍的,而且你可能很难理解其中的区别。也许这主要是源于对OAuth和OpenID Connect规范所定义的每个工件的不同目标没有清晰的认识。另外,了解这些工件最初的操作场景,对防止它们的使用出现混乱也有重要作用。尽管如此,我希望这个话题现在能更清楚一点。

回顾一下,这里是对你学到的关于你可以和不可以用ID和访问令牌做什么的一个快速总结。

ID token vs access token

如果你想看看ID和访问令牌的作用,请注册一个免费的Auth0账户,并开始用你喜欢的编程语言和框架在几分钟内为你的应用程序添加认证和授权。