A guided tour of Kerberos: Tutorial

为了更好的理解 Kerberos 中的概念和认证的大体流程,这篇文档是快速了解 Kerberos 的一篇非常棒的指南教程,原文地址为 KERBEROS PROTOCOL TUTORIAL,也可以直接访问原文地址查看。

This tutorial was written by Fulvio Ricciardi and is reprinted here with his permission. Mr. Ricciardi works at the National Institute of Nuclear Physics in Lecce, Italy. He is also the author of the Linux project zeroshell.net, where he originally published this tutorial. Thank you, Mr. Ricciardi!

本教程由 Fulvio Ricciardi 撰写,并由它的许可在这里转载。Ricciardi 先生在意大利莱切(Lecce, Italy)国家核物理研究所工作,他还是 Linux 项目 zeroshell.net 的作者,他最初在该项目上发布了本文,Thank you, Mr. Ricciardi!

Document version:	1.0.3    (11/27/2007)
Author:	Fulvio Ricciardi ([email protected])
INFN - the National Institute of Nuclear Physics
Computing and Network Services - LECCE, Italy

文档版本: 1.0.3    (2007-11-27)
作者:Fulvio Ricciardi ([email protected])
     INFN - 国家核物理计算与网络服务研究所-意大利莱切

1 介绍

Kerberos 协议目标是在开放和不安全的网络(open and insecure networks)上提供可靠的身份验证,其中属于该协议的主机之间的通信可能会被拦截。但是请注意,如果使用的计算机容易受到攻击则 Kerberos 不提供任何保证:身份验证服务、应用程序服务(imap, pop, smtp, telnet, ftp, ssh , AFS, lpr, …) 和客户端必须保持不断更新以确保请求用户和服务提供者的真实性。

以上几点说明了这一句话:“Kerberos是用于不受信任网络上的受信任主机的身份验证协议”,举例说明一下并重申一下这一概念:如果获得对服务器的特权访问的人可以复制包含密钥的文件,则 Kerberos 的策略就没有用。确实入侵者会将此密钥放在另一台计算机上,并且只需要为该服务器获得一个简单的欺骗 DNS 或 IP 地址即可将其显示为真实的服务器。

2 目的

在描述组成 Kerberos 身份验证系统的元素并查看其操作之前,下面列出了协议希望实现的一些目标:

  • 用户的密码绝不能通过网络传播;
  • 用户密码绝不能以任何形式存储在客户端计算机上:使用后必须立即丢弃;
  • 用户密码永远不要以未加密的形式存储在身份验证服务数据库(authentication server database)中;
  • 要求用户在每个工作会话中仅输入一次密码,因此用户可以透明地访问其授权使用的所有服务,而不必在此会话期间重新输入密码。此特征称为单点登录(Single Sign-On)。
  • 认证信息管理是集中式的且位于认证服务器上。应用程序服务不得包含其用户的身份验证信息,这对于获得以下结果至关重要:
    1. 管理员可以通过在单个位置执行操作来禁用任何用户的帐户,而不必在提供各种服务的多个应用程序服务上执行操作;
    2. 用户更改密码时,同时更改所有服务的密码;
    3. 没有身份验证信息的冗余,否则这些冗余信息必须在各个地方得到保护;
  • 用户不仅必须证明自己是谁,而且在被请求时,应用程序服务还必须向客户端证明其真实性,此特性称为相互认证(Mutual authentication)。
  • 完成身份验证和授权后,如果需要客户端和服务器必须能够建立加密连接。为此 Kerberos 提供了对用于加密数据的加密密钥的生成和交换的支持。

3 组件和术语的定义

此部分提供了对象和术语的定义,其知识对于随后的 Kerberos 协议描述至关重要。由于许多定义是基于其他定义的,因此作者尽可能尝试将它们排序,以便在定义术语时不提前给出术语的定义。但是,可能需要阅读本节两次(twice)才能完全理解所有术语。

3.1 领域(Realm)

术语 Realm 表示认证管理域,其目的是建立一个边界,在该边界内身份验证服务有权对用户、主机或服务进行身份验证。 这并不意味着用户和服务之间的认证必须属于同一 realm:如果两个对象是不同 Realm 的一部分,并且它们之间存在信任关系,则可以进行认证。这种特性将在下文描述为交叉认证(Cross-Authentication)。

基本上 user/service 仅在其与该 realm 的身份验证服务共享秘密(密码/密钥)时才属于该 realm。

realm 的名称区分大小写,即大写和小写字母之间有区别,但通常情况下 realm 始终以大写字母出现。在组织中使 realm 名称与 DNS域相同(也是大写字母)也是一种好习惯。 在选择 realm 名称时遵循这些提示可以大大简化 Kerberos 客户端的配置,尤其是在需要与子域(subdomains)建立信任关系时。 举例来说,如果组织属于 DNS 域 example.com,则相关 Kerberos 的 realm 最好是 EXAMPLE.COM

3.2 主体(Principal)

委托人是用于引用身份验证服务数据库中条目的名称,principal 与给定 realm 的每个用户、主机或服务相关联。 Kerberos 5 中的主体具有以下类型:

component1/component2/.../componentN@REALM

但是实际上最多使用两个组件,对于引用用户的条目 principal 是以下类型:

Name[/Instance]@REALM

该实例是可选的且通常用于更好地限定用户类型,例如管理员用户通常具有admin实例。以下是引用给用户的 principals 的示例:

[email protected]    admin/[email protected]    pluto/[email protected]

相反如果条目引用服务,则主体采用以下形式:

Service/Hostname@REALM

第一个组件是服务的名称,例如 imap、AFS、ftp。通常 “主机” 一词用于表示对计算机(telnet,rsh,ssh)的一般访问。第二部分是提供所请求服务的计算机的完整主机名(FQDN),重要的是,此组件必须与应用程序服务器 IP 地址的 DNS 反向解析完全匹配(用小写字母表示)。以下是引用服务的 principals 的有效示例:

imap/[email protected]
host/[email protected]
afs/[email protected]

应该注意的是最后一种情况是一个例外,因为第二个组件不是主机名,而是主体所指的 AFS cell 的名称。 最后有些 principals 不涉及用户或服务,但在身份验证系统的操作中起作用。一个整体示例是 krbtgt/REALM@REALM 及其关联的密钥,用于加密票据授予票据(我们将在后面介绍)。

在 Kerberos 4 中组件最多不能超过两个,它们之间用字符.分隔而不是/,而引用服务的 principals 中的主机名是简称即不是 FQDN。以下是有效的示例:

[email protected]    [email protected]    [email protected]

3.3 票据(Ticket)

ticket 是客户端提供给应用程序服务器以证明其身份真实性的东西,ticket 是由身份验证服务器发行的,并使用其预定服务的密钥进行加密。由于此密钥是仅在身份验证服务器和提供服务的服务器之间共享的秘密,因此即使请求 ticket 的客户端也无法知道它或更改其内容,ticket 中包含的主要信息包括:

  • 请求用户的 principal(通常是用户名);
  • principal 服务的目的;
  • 可以使用 ticket 的客户端计算机的 IP 地址,在 Kerberos 5 中此字段是可选的,也可以是多个字段,以便能够在 NAT 或多宿主下运行客户端。
  • ticket 有效期开始的日期和时间(以时间戳格式);
  • ticket 的最大寿命
  • 会话密钥(其基本作用如下所述);

每个 ticket 都有一个有效期(通常为10小时),这是必不可少的,因为身份验证服务器不再对已发出的 ticket 具有任何控制权,即使 realm 管理员可以随时阻止某个用户发布新 ticket,也不能阻止用户使用他们已经拥有的 ticket,限制 ticket 寿命的原因是为了限制随时间的滥用。

ticket 包含许多其他信息和标志,这些信息和标志描述了他们的行为,但我们在这里不做介绍。在了解身份验证系统的工作原理之后,我们将再次讨论 ticket 和 标志。

3.4 加密(Encryption)

如您所见 Kerberos 通常需要对在身份验证的各个参与者之间传递的消息(ticket 和身份验证)进行加密和解密。重要的是要注意 Kerberos 仅使用对称密钥加密(换句话说相同的密钥用于加密和解密)。某些项目(例如 pkinit)正在积极引入公钥系统,以便通过呈现与经认证的公钥相对应的私钥来获得初始用户身份验证,但是由于目前尚无标准,因此我们暂时跳过此讨论。

3.4.1 加密类型

Kerberos 4 实现了一种加密类型即56位 DES,这种加密的弱点以及其他协议漏洞已使 Kerberos 4 过时了。但是 Kerberos 版本5 不能确定支持的加密方法的 number 或类型。支持并最佳地协商各种类型的加密是每个特定实现版本的任务,但是协议的这种灵活性和可扩展性加剧了 Kerberos 5 各种实现之间的互操作性问题。为了让用了不同实现的客户端以及应用程序和身份验证服务器能够互操作,它们必须至少具有一种共同的加密类型。与 Kerberos 5 的 Unix 实现和 Windows Active Directory 中存在的实现之间的互操作性相关的困难就是一个典型的例子。实际上 Windows Active Directory 支持有限数量的加密,并且与 Unix 一样仅具有 56位的 DES。尽管必须保证互操作性,但是尽管众所周知的风险也要求使后者保持启用状态。随后使用 MIT Kerberos 5 的 1.3 版本解决了该问题,此版本引入了 RC4-HMAC 支持,该支持也存在于 Windows 中,并且比 DES 更安全。在受支持的加密中(但不是 Windows),值得一提的是三重 DES(3DES)以及较新的 AES128 和 AES256。

3.4.2 加密密钥(Encryption key)

如上所述 Kerberos 协议的目的之一是防止用户密码以未加密的形式存储,甚至在身份验证服务器数据库中也是如此。考虑到每种加密算法都使用其自己的密钥长度,很明显如果不强迫用户为支持的每种加密方法使用固定大小的不同密码,则加密 key 不能是密码。 由于这些原因引入了 string2key 函数,该函数将未加密的密码转换为适合于要使用的加密类型的加密密钥。每次用户更改密码或输入密码进行身份验证时都会调用此方法。string2key 称为哈希函数,这意味着它是不可逆的:鉴于加密密钥无法确定生成该密钥的密码(除非通过暴力),著名的哈希算法是 MD5 和 CRC32。

3.4.3 盐(Salt)

与版本4不同在 Kerberos 5 中引入了密码盐的概念。 这是在应用 string2key 函数获取密钥之前要与未加密密码连接的字符串。Kerberos 5 使用与 salt 相同的 principal 用户:

Kpippo = string2key(Ppippo + "[email protected]")

Kpippo 是用户 pippo 的加密密钥,而 Ppippo 是用户的未加密密码。这种盐具有以下优点:

  • 属于同一 realm 并且具有相同的未加密密码的两个 principals 仍然具有不同的密钥。 例如假设有一个管理员负责日常工作([email protected])且一个负责管理工作(pippo/[email protected]),为方便起见,此用户很可能为两个 principals 设置了相同的密码,盐的存在保证了相关密钥是不同的。
  • 如果用户在不同的 realm 中有两个帐户,则经常会出现两个 realm 的未加密密码相同的情况,由于盐的存在,一个 realm 中一个帐户的可能破坏不会自动导致另一个 realm 被妥协。

可以配置一个 null 盐来与 Kerberos 4 兼容,反之亦然,为了与 AFS 兼容可以配置一个盐,它不是 principal 的完整名称,而仅仅是 cell 的名称。

在讨论了加密类型、string2key 和 salt 的概念之后,可以检查以下观察的准确性:为了使各种 Kerberos 实现之间具有互操作性,仅协商一种通用的加密类型是不够的,但是同样也需要使用相同类型的 string2key 和 salt。

还需要注意的是,在解释 string2key 和 salt 的概念时仅引用了用户 principals 而没有引用服务器的 principals,原因很明显:即使服务与身份验证服务器共享秘密,它也不是未加密的密码(谁会输入它呢?),而是由管理员在 Kerberos 服务器上生成的密钥,其存储为它所提供服务的服务器上。

3.4.4 key 版本号 - kvno (Key Version Number)

当用户更改密码或管理员更新应用程序服务器的密钥时,此更改将通过增加计数器来记录,标识密钥版本的计数器的当前值称为密钥版本号或更简称为 kvno

3.5 密钥分发中心 - KDC(Key Distribution Center)

我们已经谈到认证服务器,由于它是用户和服务认证中涉及的基本对象,因此我们现在将对其进行更深入的研究,但不涉及其操作的所有细节,而将其作为协议操作部分的主题。

Kerberos 环境中的身份验证服务器基于其用于访问服务的 ticket 分发功能,被称为密钥分发中心,或更简称为 KDC。由于它完全驻留在单个物理服务器上(它通常与单个进程重合),因此可以从逻辑上将其分为三个部分:数据库、身份验证服务器(AS)和 票证授予服务器(TGS),让我们简单地看一下它们。

注意:可以使服务器在 Master/Slave(MIT 和 Heimdal)或多主控结构(Windows Active Directory)中的 realm 内为冗余。协议没有描述如何获得冗余,而是取决于所使用的实现方式,在此将不进行讨论。

3.5.1 数据库(Database)

数据库是与用户和服务关联的条目(entries)的容器,即使经常使用术语 principal 作为条目的同义词,我们也使用 principal(即条目的名称)来引用条目,每个条目包含以下信息:

  • 条目所关联的 principal;
  • 加密密钥和相关的 kvno;
  • 与 principal 关联的 ticket 的最大有效期;
  • 可以更新与 principal 相关联的 ticket 的最长时间(仅 Kerberos 5);
  • 表征 ticket 行为的属性或标志;
  • 密码到期日期;
  • principal 的到期日,之后将不发行 ticket。

为了使窃取数据库中存在的密钥更加困难,实现中使用与主 K/M@REALM 关联的主密钥(master key)对数据库进行加密。 即使是任何数据库转储、用作备份或从 KDC 主服务器向从属服务器传输也都使用此密钥进行加密,为了重新加载它们必须知道该密钥。

3.5.2 身份认证服务 - AS(Authentication Server)

身份验证服务器是 KDC 的一部分,当尚未经过身份验证的用户必须输入密码时它会响应来自客户端的初始身份验证请求。响应于身份验证请求,AS发出一个特殊的 ticket,称为票证授予票据(Ticket Granting Ticket),或更简单地说是 TGT,与之关联的主体是 krbtgt/REALM@REALM。如果用户实际上就是他们所说的真实身份(我们将在后面看到他们的演示方式),则可以使用 TGT 获得其他服务票证而无需重新输入密码。

3.5.3 票据授权服务 - TGS(Ticket Granting Server)

票证授予服务器是 KDC 组件,它通过有效的 TGT 将服务票证分发给客户端,从而保证了身份的真实性,以便在应用程序服务器上获得请求的资源。TGS 可以看作是一个应用程序服务器(假设要访问它,必须提供 TGT),该服务器提供服务票证的发行服务,重要的是不要混淆缩写 TGT 和 TGS:第一个表示票证,第二个表示票务。

3.6 会话密钥(Session Key)

如我们所见用户和服务与 KDC 共享一个秘密。对于用户此秘密是从其密码派生的密钥,而对于服务这是其密钥(由管理员设置)。这些密钥被称为长期密钥,因为它们在工作会话更改时不会更改。

但是,至少在客户端在服务器上打开工作会话的时间内,用户还必须与服务共享一个秘密:发行票证时由 KDC 生成的此密钥称为会话密钥。用于服务的副本由 KDC 封装在票证中(无论如何,其应用服务器知道长期密钥并可以对其进行解码并提取会话密钥),而用于用户的副本则与用户的长期密钥封装在加密的数据包中。会话密钥在证明用户的真实性方面起着基本作用,我们将在以下段落中看到。

3.7 身份验证(Authenticator)

即使用户 principal 存在于票证中并且只有应用程序服务器才能提取且可能管理此类信息(因为票证已使用服务的密钥进行了加密),但这不足以保证客户端的真实性。当合法客户将票证发送到应用程序服务器时,冒名顶替者可以捕获(记住一个假设在开放且不安全的网络下)票证,并在适当时机将其发送给非法获取服务。另一方面,将机器的 IP 地址包括在可以使用的地方不是很有用:众所周知,在开放和不安全的网络中,地址很容易被伪造。为了解决该问题,必须利用这样一个事实,即客户端和服务器至少在会话期间,具有一个只有他们才知道的会话密钥(KDC 自生成它以来也知道它,但是从定义上来说它是受信任的!!!)。因此将应用以下策略:客户端与包含票证的请求一起添加另一个包(身份验证器),其中包含用户 principal 和时间戳(在那时),并使用会话密钥对其进行加密;必须提供服务的服务器在收到此请求后,将第一张 ticket 解包提取会话密钥,如果用户确实是他/她所说的话,则服务器可以对验证者进行解密以提取时间戳。如果后者与服务器时间相差少于2分钟(但是可以配置容差)则认证成功。这强调了属于同一 realm 的机器之间同步的重要性。

3.8 重播缓存(Replay Cache)

冒名顶替者有可能同时窃取票证和验证者并在验证者有效的2分钟内使用它们,这是非常困难但并非不可能的。为使 Kerberos 5 解决这个问题,引入了重播缓存。在应用程序服务器中(但在 TGS 中),也可以记住最近2分钟内到达的身份验证器,如果它们是副本则可以拒绝它们。这样只要冒名顶替者不够聪明,无法复制票证和验证器并使它们在合法请求到达之前到达应用程序服务器,就可以解决问题。 这确实是一个骗局,因为当冒名顶替者可以访问该服务时真实用户将被拒绝。

3.9 凭据缓存(Credential Cache)

客户端永远不会保留用户的密码,也不会记住通过应用 string2key 获得的密钥:它们用于解密来自 KDC 的答复并被立即丢弃。 但是另一方面,要实现单点登录(SSO)特性,即要求用户每个工作会话仅输入一次密码,则必须记住票证和相关的会话密钥,该数据的存储位置称为凭据缓存,该高速缓存需要放置的位置并不取决于协议,而是因实现方式而异。通常出于可移植性目的,它们位于文件系统(MIT 和 Heimdal)中。在其他实施方式(AFS 和 Active Directory)中,为了在出现易受攻击的客户端的情况下提高安全性,将凭据缓存放置在仅内核可访问且不能在磁盘上交换的内存区域中。

4 Kerberos 操作

最后获得了前面段落中描述的概念之后,可以讨论 Kerberos 的工作方式。我们将通过列出并描述身份验证期间在客户端和 KDC 之间以及客户端和应用程序服务器之间传递的每个数据包来完成此操作。在这一点上,重要的是要强调应用程序服务器永远不要直接与密钥分发中心进行通信:票据服务即使由 TGS 打包,也只能通过希望访问它们的客户端才能到达服务。下面将列出我们将讨论的消息(另请参见下图):

  • AS_REQ 是初始用户身份验证请求(即使用 kinit 发出的请求)。此消息定向到称为身份验证服务器(AS)的 KDC 组件;
  • AS_REP 是身份验证服务器对先前请求的答复。基本上它包含 TGT(使用 TGS 密钥加密)和会话密钥(使用发出请求的用户的密钥加密);
  • TGS_REQ 是从客户端到票据授权服务器(TGS)的服务票证请求。该数据包包括从先前的消息中获得的 TGT 和由客户端生成并使用会话密钥加密的身份验证器。
  • TGS_REP 是票据授权服务器对先前请求的答复。位于其中的是请求的服务票证(使用服务的密钥加密)和由 TGS 生成并使用 AS 生成的先前会话密钥加密的服务会话密钥;
  • AP_REQ 是客户端发送到应用程序服务器以访问服务的请求。这些组件是从 TGS 获得的带有先前答复的服务票证,以及由客户端再次生成的身份验证器,但是这次使用服务会话密钥(由TGS生成)进行了加密;
  • AP_REP 是应用程序服务器提供给客户端的答复,以证明它确实是客户端期望的服务器。并非总是请求此数据包,客户端仅在需要相互认证时才向服务器请求。
    Kerberos 请求过程图

现在参考 Kerberos 5 更详细地描述每个先前的阶段,但会指出与版本4的区别。但是应该记住 Kerberos 协议相当复杂,本文档不作为指南。 对于那些想知道确切的操作细节的人(无论如何,这些细节已经在 RFC1510 中编写了)。下面的讨论被有意抽象的,但对于那些检查 KDC 日志的人员来说理解各种身份验证转换和发生的任何问题就足够了。

注意:后续段落将未加密的数据括在圆括号()中,将加密的数据括在大括号{}中:( x, y, z )表示x,y,z未加密;{ x, y, z }K 表示使用对称密钥 K 一起加密了x,y,z。同样重要的是要注意,包中列出的组件的顺序与实际消息(UDP 或 TCP)中的实际顺序无关。讨论非常抽象,如果您希望获得更多详细信息,请参阅具有描述性协议 ASN.1 的背景知识的 RFC1510。

4.1 身份验证服务请求 - AS_REQ(Authentication Server Request)

在此阶段中称为初始身份验证请求,客户端(kinit)向 KDC(更具体地为 AS)请求票证授予票证。该请求是完全未加密的,如下所示:

AS_REQ = (PrincipalClient , PrincipalService , IP_list , Lifetime )

其中:

  • PrincipalClient 是与寻求身份验证的用户关联的 principal(例如[email protected]);
  • PrincipalService 是与票证要求的服务关联的 principal,因此是字符串krbtgt/REALM@REALM(请参阅 Note *);
  • IP_list 是一个 IP 地址列表,指示可以在其中使用将要发出的票证的主机(请参阅 Note **);
  • Lifetime 是要发行的票证的最大有效时间(要求的)。

Note *:将 PrincipalService 添加到初始身份验证请求似乎是多余的,因为这将一直设置为 TGS 主体,即krbtgt/REALM@REALM。但是事实并非如此,实际上计划在工作会话中仅使用一项服务的用户不会使用单一登录,而是可以直接向 AS 请求该服务的票证从而跳过随后的请求到TGS。从操作角度来看(MIT 1.3.6),以下命令已足够:kinit -S imap/[email protected] [email protected]

Note **:IP_list 也可以为空,在这种情况下任何机器都可以使用相应的票证。这解决了那些在 NAT 下的客户端的问题,因为它们的请求将以与请求用户的源地址不同但与进行 NAT 的路由器相同的源地址到达服务。相反对于具有多个网卡的计算机,IP_list 应该包含所有网卡的 IP 地址:实际上很难预先预测提供服务的服务器将与哪个连接联系。

4.2 身份验证服务回复 - AS_REP(Authentication Server Reply)

当前一个请求到达时,AS 会检查 KDC 数据库中是否存在 PrincipalClient 和 PrincipalService:如果两者中至少有一个不存在则会向客户端发送错误消息,否则 Authentication Server如下处理答复:

  • 它随机创建一个会话密钥,该密钥将是客户端和 TGS 之间共享的秘密。假设为 SKTGS
  • 它将创建 TGT 并将其放入请求用户的 principal,principal 服务(通常为 krbtgt/REALM@REALM,,但请阅读上一段的 Note *),IP地址列表(前三部分信息是在它们通过 AS_REQ 数据包到达时进行复制),
    时间戳格式的日期和时间(KDC 的日期和时间),lifetime(请参阅 Note *)以及最后的会话密钥。 SKTGS; 因此 TGT 如下所示:

TGT = (PrincipalClient , krbtgt/REALM@REALM , IP_list , Timestamp , Lifetime , SKTGS )

  • 它生成并发送包含以下内容的答复:先前创建的票证,已使用服务的密钥进行了加密(我们将其称为KTGS); 服务 principal、timestamp、lifetime 和 会话密钥均使用请求服务的用户的密钥进行了加密(我们将其称为KUser),总结为:

AS_REP = {PrincipalService , Timestamp , Lifetime , SKTGS}KUser { TGT }KTGS

该消息似乎包含冗余信息(PrincipalService、Timestamp、Lifetime 和会话密钥)。但是事实并非如此:由于 TGT 中存在的信息是使用服务器的密钥加密的,客户端无法读取它,因此需要重复该信息。 此时当客户端收到回复消息时,它将要求用户输入密码,盐与密码连接在一起,然后应用 string2key 函数:使用生成的密钥,尝试使用存储在数据库中的用户的密钥来解密由 KDC 加密的消息部分。 如果用户确实是他/她说的人,并因此输入了正确的密码,则解密操作将成功,因此可以提取会话密钥,并将 TGT(保持加密状态)存储在用户的凭据缓存中。

Note *:实际 lifetime,即票证中的实际 lifetime 是以下值中的最小值:客户端请求的 lifetime、用户 principal 中包含的 lifetime 以及服务 principal 中包含的 lifetime。实际上,在实现方面,可以从 KDC 的配置中设置另一个限制,并将其应用于任何票证。

4.3 票据授权服务请求 - TGS_REQ(Ticket Granting Server Request)

此时,用户已经证明是他/她所说的那个人(因此,在他/她的凭证缓存中有一个 TGT 和会话密钥 SKTGS,想要访问该服务但还没有合适的 ticket,则发送一个 TGS_REQ,其构造如下:

  • 使用用户 principal、客户端机器时间戳创建身份验证器,并使用与 TGS 共享的会话密钥对所有内容进行加密,即:

Authenticator = { PrincipalClient , Timestamp} SKTGS

  • 创建一个请求数据包,其中包含:需要票证且未加密生命周期的服务 principal;该票证已使用TGS的密钥加密的 TGT;和刚刚创建的身份验证器。概括起来如下:

TGS_REQ = ( PrincipalService , Lifetime, Authenticator) { TGT }KTGS )

4.4 票据授权服务重放 - TGS_REP(Ticket Granting Server Replay)

当先前的请求到达时 TGS 首先验证 KDC 数据库中是否存在所请求的服务的主体(PrincipalService):如果存在,则使用 krbtgt/REAM@REALM 的字符串打开 TGT 并提取会话密钥SKTGS 作为要解密的身份验证器。对于要发行的票据服务,进行检查以下情况是否有肯定的结果:

  • TGT 尚未过期;
  • 身份验证器中存在的 PrincipalClient 与 TGT 中存在的 PrincipalClient 相匹配;
  • 验证者不存在于重播缓存中并且尚未过期;
  • 如果IP_list不为null,它将检查请求数据包(TGS_REQ)的源 IP 地址是否为列表中包含的 IP 地址之一;

先前检查的条件证明 TGT 确实属于发出请求的用户,因此 TGS 开始按以下方式处理答复:

  • 它随机创建一个会话密钥,该密钥将是客户端和服务之间共享的秘密。假设为 SKService
  • 它创建服务票证,将请求用户的 principal、服务的 principal、IP地址列表、时间戳格式的(KDC的)日期和时间、生存期(作为TGT生存期与与服务 principal 相关联),最后是会话密钥 SKService。被称为 TService 的新票证是:

TService = ( PrincipalClient , PrincipalService , IP_list , Timestamp , Lifetime , SKService)

  • 它发送包含以下内容为回复消息:先前创建的票证、已使用服务密钥加密(我们将其称为 KService )、服务 principal、时间戳、生存期 和 新会话密钥都使用从 TGT 提取的会话密钥进行了加密。 概括起来如下:

TGS_REP = {PrincipalService , Timestamp , Lifetime , SKService}SKTGS { TService }KService

当客户端收到答复后,在凭据缓存中具有会话密钥 SKTGS,它可以解密包含其他会话密钥的消息部分,并将其与服务票证 TService 一起存储,但是该票证仍保持加密状态。

4.5 应用服务请求 - AP_REQ(Application Server Request)

具有访问服务的凭据(即票证和相关会话密钥)的客户端可以通过 AP_REQ 消息向应用程序服务器请求对资源的访问。应该牢记的是与先前涉及 KDC 的消息不同,AP_REQ 不是标准的而是根据应用程序而有所不同,因此应用程序程序员负责建立策略,客户端将使用该策略使用其凭据向服务器证明其身份。 但是,我们可以通过示例考虑以下策略:

  • 客户端创建一个包含用户 principal 和 时间戳的身份验证器,并使用与应用程序服务器共享的会话密钥 SKService 加密所有内容,即:

Authenticator = { PrincipalClient , Timestamp }SKService

  • 它创建一个包含服务票证 TService 的请求数据包,该服务票证已使用其密钥和刚刚创建的身份验证器进行了加密,概括起来如下:

AP_REQ = Authenticator { TService }KService

当先前的请求到达时,应用程序服务器使用所请求服务的密钥打开票证,并提取会话密钥 SKService,它将其用于解密身份验证器。为了确定发出请求的用户是真实的,并因此授予对该服务的访问权限,服务器将验证以下条件:

  • 票据尚未过期;
  • 身份验证器中存在的 PrincipalClient 与票证中存在的 PrincipalClient 相匹配;
  • 验证者不存在于重播缓存中并且尚未过期;
  • 如果 IP_list(从票证中提取)不为空,则检查请求包的源 IP 地址(AP_REQ)是否为列表中包含的IP地址之一;

注意:先前的策略与票证授权服务器用来检查请求服务票证的用户的真实性的策略非常相似,但这不足为奇,因为我们已经解释了 TGS 可以被视为应用服务器,其服务是向那些使用 TGT 证明自己身份的人提供票证。

4.6 应用服务重放 - AP_REP (缺失)


4.7 身份预认证

从 Authentication Server Reply(AS_REP)的说明中可以看出,在分发票证之前 KDC 只需检查数据库中是否存在请求用户和服务提供者的 principal。然后尤其是在涉及到 TGT 的请求时这甚至会更加容易,因为 krbtgt/REALM@REALM 确实存在,因此只要知道用户的 principal 就可以通过简单的初始身份验证请求获得 TGT 就足够了。显然,如果请求来自一个非法用户,则无法使用该 TGT,因为他们不知道密码,也无法获取用于创建有效身份验证器的会话密钥。但是以这种简单方式获得的该票证可能会遭受暴力攻击,以试图猜测该票证旨在提供的服务的长期密钥。显然,即使使用当前的处理能力,猜测服务的秘密也不是一件容易的事,然而 对于 Kerberos 5 还是引入了预认证概念以增强安全性。因此,如果 KDC 策略(可配置)请求对初始客户端请求进行身份预验证,则身份验证服务器将以错误包答复,指示需要进行身份预验证。鉴于错误,客户端要求用户输入密码并重新提交请求,但是这次添加了用用户长期密钥加密的时间戳,我们知道这是通过在加盐后将 string2key 应用于未加密的密码(如果有)来获得的。这次,由于KDC知道用户的秘密密钥,因此它尝试解密的请求中存在时间戳,如果成功,并且时间戳符合要求,即包含在已建立的容限内,则它判断请求的用户是真实的,并且身份验证过程可以正常继续。

重要的是要注意,身份预验证是 KDC 策略,因此协议不一定要求它。在实现方面默认情况下 MIT Kerberos 5 和 Heimdal 禁用了预身份验证,而 Windows Active Directory 和 AFS kaserver(这是经过预身份验证的 Kerberos 4)中的 Kerberos 会请求它。

5 深入理解票据(Tickets in-depth)

如前面所述,既然已经讨论了 KDC 的操作以及身份验证所涉及的主机之间的消息,那么现在我们来看看票据。这些取决于它们是否在其中设置了属性(也称为标志),它们以某种方式运行。我在下面列出了最重要的票据类型,即使考虑到我在谈论协议也不完全正确,我仍将引用 MIT Kerberos 5 的 1.3.6 版(足以使事情变得清楚)。

5.1 初始化票据(Initial tickets)

初始化票据是直接从 AS 获得的,即当用户必须通过输入密码进行身份验证时,从这里可以推断出,TGT 始终是初始票据。另一方面,服务票据由 TGS 在提供 TGT 时分发,因此不是初始票据。但是该规则有一个例外:为了保证用户仅在几秒钟之前输入密码,某些 Kerberos 应用程序可能会要求服务票据是初始的。在这种情况下,尽管不是 TGT,但票证是从 AS 而不是 TGS 请求的,因此是初始票据。在操作方面,用户 pippo 希望获得机器 mbox.example.com 上 imap服务 的初始票证(因此无需使用 TGT),因此使用以下命令:

[pippo@client01 pippo]$ kinit -S imap/[email protected] [email protected]
Password for [email protected]: 
[pippo@client01 pippo]$ 
[pippo@client01 pippo]$ klist -f
Ticket cache: FILE:/tmp/krb5cc_500
Default principal: [email protected]

Valid starting     Expires            Service principal
01/27/05 14:28:59  01/28/05 14:28:39  imap/[email protected]
        Flags: I

Kerberos 4 ticket cache: /tmp/tkt500
klist: You have no tickets cached

应该注意标志 I 的存在,表明它是一张初始票。

5.2 可续票据(Renewable tickets)

可以将可续票据重新提交给 KDC 进行更新,即在整个生命周期内将其重新分配。显然仅当票据尚未过期且未超过最大续订时间(在密钥分发中心数据库中设置)时,KDC 才会接受续订请求。能够更新票据的原因在于出于安全原因需要具有短时票据,而不必长时间重新输入密码:例如假设一项工作必须处理数天且无需任何人工干预 。在以下示例中,pippo 要求购买一张门票,该门票最多可使用一小时,但可续签8天:

kinit -l 1h -r 8d pippo
Password for [email protected]:
[pippo@client01 pippo]$ 
[pippo@client01 pippo]$ klist -f
Ticket cache: FILE:/tmp/krb5cc_500
Default principal: [email protected]

Valid starting     Expires            Service principal
01/27/05 15:35:14  01/27/05 16:34:54  krbtgt/[email protected]
        renew until 02/03/05 15:35:14, Flags: RI


Kerberos 4 ticket cache: /tmp/tkt500
klist: You have no tickets cached
while for pippo to renew his ticket without re-entering the password:

[pippo@client01 pippo]$ kinit -R
[pippo@client01 pippo]$ 
[pippo@client01 pippo]$ klist -f
Ticket cache: FILE:/tmp/krb5cc_500
Default principal: [email protected]

Valid starting     Expires            Service principal
01/27/05 15:47:52  01/27/05 16:47:32  krbtgt/[email protected]
        renew until 02/03/05 15:35:14, Flags: RIT


Kerberos 4 ticket cache: /tmp/tkt500
klist: You have no tickets cached

5.3 可转发票据(Forwardable tickets)

假设我们在具有相关 TGT 的机器上进行工作会话,并希望从该机器登录另一台机器并保留 ticket,可转让票据是解决此问题的方法。从一台主机转发到另一台主机的票据本身是可转发的,因此,一旦通过身份验证,便可以在所有所需计算机上访问登录名,而不必重新输入任何密码。

为了在没有 Kerberos 的情况下获得相同的结果,有必要使用安全性低得多的方法,例如 rsh 或 ssh 的公钥身份验证。但是,后一种方法在用户主目录位于网络文件系统(例如 NFS 或 AFS)上的系统可能不可行,因为私钥(应为私有)将通过网络。

6 交叉认证(Cross Authentication)

我们已经提到了属于某个 realm 的用户验证和访问另一个 realm 的服务的可能性。称为交叉身份验证的此特征基于以下假设:所涉及的 realm 之间存在信任关系,这可能是单向的,这意味着 realm A 的用户可以访问 realm B 的服务,但反之则不能;或者是双向的,正如人们可能期望的那样,也可以相反。在以下各文章段落中,我们将研究交叉身份验证,将信任关系分为 直接、传递和分层。

6.1 直接信任关系(Direct trust relationships)

这种类型的信任关系是基本的,是交叉身份验证的基础并且用于构造其他两种类型的关系,我们将在后面介绍。当 realm B 的 KDC 直接信任 realm A 的 KDC 时,就会发生这种情况,从而允许后者的用户访问其资源。从实际的角度来看,直接信任关系是通过使两个参与的 KDC 共享一个密钥来获得的(如果需要双向信任,则密钥将变为两个)。为此,引入了远程 TGT 的概念,在两个 realm A 和 B 的示例中,TGT 的形式为 krbtgt/B@A,并使用相同的密钥添加到两个 KDC 中,该密钥是确保两个 realm 之间信任的秘密。显然,要使其成为双向的(即 A 也信任 B),有必要在两个 KDC 中创建远程 TGT krbtgt/A@B,并将它们与另一个密钥相关联。

我们将在下面的示例中很快看到,引入远程 TGT 使得交叉身份验证成为常规 intra-realm 身份验证的自然概括:这说明只要接受一个 realm 的 TGS 可以验证另一 realm 的 TGS 发出的远程 TGT,则先前对 Kerberos 操作的描述将继续有效。请注意,当远程 TGT 不是由 AS 发布时(由本地发布,而是由本地票据授权服务器在发布本地 TGT 时发生)时,会出现形式上的异常。

现在让我们看一个例子来阐明所有这些,假设示例 EXAMPLE.COM 的用户 pippo(其关联 principal 为 [email protected])希望通过 ssh 访问属于 TEST.COM realm 的 pluto.test.com 服务器:

  • 如果 Pippo 在 EXAMPLE.COM realm 中尚未具有 TGT,他将发出初始身份验证请求(kinit)。显然答复来自其 realm 的 AS。
  • 他给出了 ssh [email protected] 命令,该命令应在 pluto.test.com 上打开远程 shell 而无需重新输入密码。
  • ssh 客户端对 DNS 进行两次查询:计算出 pluto.test.com 的 IP,然后对刚获得的地址进行反向查询,以获取规范形式的主机名(FQDN)(在这种情况下它与 pluto.test.com 一致);
  • 然后,由于先前的结果 ssh 客户端意识到目的地不属于用户的 realm,因此向该 realm 的 TGS 询问 EXAMPLE.COM(请注意,它为此向其 realm 的 TGS 询问)以获得远程TGT krbtgt/[email protected];
  • 通过远程 TGT,它向 TEST.COM realm 的 TGS 索要 host/[email protected] 服务票据;
  • TEST.COM 票据授权服务收到请求时,它将检查其数据库中是否存在 principal krbtgt/[email protected],它可以用来验证信任关系。
    如果此验证是肯定的,则最终会发出服务票据(使用 host/[email protected] 的密钥加密),然后 pippo 将其发送到主机 pluto.test.com 以获取远程 shell。

6.2 传递信任关系(Transitive trust relationships)

当必须进行交叉身份验证的 realms 的数量增加时,要交换的密钥的数量将平方增加。 例如如果有5个 realms 并且关系必须是双向的,则管理员必须生成20个密钥(将5个元素的组合乘以2乘以2)。

为解决此问题,Kerberos 5 在信任关系中引入了可传递性:如果 realm A 信任 realm B,realm B 信任 realm C,则 A 将自动信任 C。此关系属性将大大减少密钥的数量(即使身份验证次数增加)。

但是,仍然存在一个问题:如果身份验证路径(capath)不是直接的则客户端无法猜测。 因此,必须通过在每个客户端的配置中创建一个特殊的节([capaths])来告知他们正确的路径。 这些路径也必须是 KDC 已知的,KDC 将使用它们来检查运转。

6.3 分层信任关系(Hierarchical trust relationships)

如果在组织内部使用以大写字母表示 DNS 域名称的 realm 的约定(强烈建议选择),并且如果后者属于一个层次结构,则 Kerberos 5 将支持(具有层次结构的)具有信任关系的相邻 realm(自然而然,假定的信任必须由适当的密钥来支持),并且将自动构造(无需附加功能)可传递认证路径。 但是,管理员可以通过强制客户端配置中的限制来更改此自动机制(例如,出于效率方面的考虑)。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章