之所以要翻译这篇文章,是因为提到了一些通常于对Kerberos协议简介性质的文章所没有提到的细节,而这些细节对于理解Kerberos的工作原理,以及Kerberos协议实现的使用都是很有必要的。
1.3 组件和术语的定义
这一节给了关于对象和术语的定义,对于这些的了解对于接下来关于Kerberos协议的描述是非常关键的。因为有些定义是基于其它定义的,所以我会尽量尝试按照顺序来讲解它们,以使得先给出它们的含义再给出定义(译注:这里原文有些怪怪的,不过大概意思就是按照术语出现的先后来解释它们)。尽管如此,把这一节多读两篇来完全理解这些术语也是有必要的。
1.3.1 Realm
"realm"这个术语表示一个认证管理域(译注,意思是:属于同一个域的用户使用同样的认证方案。这个可以见照kdc.conf来理解,在kdc.conf中,很多配置 项是每个realm不同的,比如database的位置,ACL设置 等)。realm用来创建认证的边界,在属于一个认证服务的边界内,这个认证服务才有权利认证一个用户,主机或者服务。但这并不意味着如果一个用户和一个服务属于不同的realm,它们就无法互相认证,如果它们不在同一个realm中,但是它们所属的realm有信任关系,那么认证就可以通过。本文下面会讲到这个被称为"交叉认证 Cross-Authentiation的机制。
基本上可以认为,只有当一个用户/服务和一个域的认证服务共享一个密秘(密码/密钥)时,这个用户/服务才属于这个域。
realm的名字是大小写敏感的,也就是说,大写和小写字符是有区别的,全是通常的realm的名字都用大写字母。另一个好的实践是,在一个组织中,让realm名字和DNS域名一致(但是要大写)。在选择realm名字的时候遵循这些建议,将会显著减少Kerberos客户端的配置,而且如果想要在子域(subdomain)间建立信任关系,这样配置是首要的。比如,如果一个组织属于DNS域example.com,那么最好把相关的Kerberos realm配成EXAMPLE.COM。
1.3.2 Principal
一个Principal就是一个名字,这个名字用于引用认证服务数据库中的一个条目。一个Principal和一个特定realm的用户、主机、或者服务相关联。Kerberos5中的一个principal有以下的形式:
component1/component2/.../[email protected]
但是,在实践中,最多只使用两个component。对于引代一个用户的条目,它的principal是这样的形式:
Name[/Instance]@REALM
其中Instance是可选 的,通常用于更好地限定用户的类型。比如,一个管理员用户通常会有admin instance(译注:即Name/[email protected]这种形式)。
下面是指代用户的 一些principal的例子:
[email protected] admin/[email protected] pluto/[email protected]
如果,这个条目指代的是服务,那么principal就需要有以下的形式:
Service/[email protected]
第一部分是service的名字,比如imap, AFS, ftp. 通常‘host‘这个名字被用于指明对一台机器的通用的访问(telnent, rsh, ssh)。
第二个component是提供这个服务的机器的完全主机名(FQDN)。这个 component跟DNS对应用服务器的IP地址进行逆向解析后得到的主机名
完全一致是很重要的。下面是指代服务所用的principal的例子:
imap/[email protected]
host/[email protected]
afs/[email protected]
需要指出的是最后一条是一个例外,因为它的第二个compoment不是主机名,而是AFS cell的名字。最后,有一些principal即不用来指代用户,
也不用于指代服务,而是Kerberos认证系统的操作中扮演一个角色。一个首要的例子就是krbtgt/[email protected],这个principal的密钥会
被用来加密Tikcet Granting Ticket(我们下面会讲到这个)(译注,krb就是指kerberos, tgt就是指Ticket Granting Ticket,所以这个特殊的principal就是
krbtgt。这个principal的密钥会被用到加密TGT,只有KDC拥有krbtgt的密钥,所以TGT才不能被伪造).
对于Kerberos 4,永远不能有超过两个的components,并且它们之间的分隔符是".", 而不是"/",并且用于指代服务的principal的hostname是短
的那种,不是FQDN。下面是可用的例子:
[email protected] [email protected] [email protected]
1.3.3 Ticket
Ticket是客户端提供给应用服务器用于表明自己真实身份的东西(译注:这是Kerberos的最终目的,大家可以想一下是Kerberos是怎么基于
对称密码算法做到这一点的)。Ticket是认证服务器(AS, Authentication Server)分发的,并且使用客户端想要访问的服务的密钥加密。由于
加密ticket使用的密钥是AS和提供服务的服务器之间的秘密,因此即使请求ticket的客户端也不能知道或更改它的内容(译注:所以, ticket的
准确的概念是指用service的密钥加密前的内容,而不是加密后被client提交给service 所在server的那些数据)。ticket包括的主要信息包括:
- 发出请求的用户的principal(通常就是用户名)
- 想要访问的服务的principal
- 想要使用这个ticket的客户端所在机器的IP地址。在Kerberos 5中这个field是可选的,并且可是有多个,以便可以在NAT或者多宿主主机(multihomed)下使用
- 准备启用ticket的日期和时间(以时间戳的形式)(The date and time when the tickets validity commences)
- ticket的最大生存时间
- 会话密钥(session key)(这个密钥有非常重要的作用,在下面将会描述它的角色)
每个ticket都会有一个过期时间(通常是10小时)。这非常关键,因为AS无法控制一个已经分发去出的ticket(译注:因为AS和提供service的server之间是没有联系的。因此,AS是没办法主动销毁一个已经分发出去的ticket的)。尽管realm的管理员可以在任何时间停止给特定的用户分发新的ticket,但是却无法阻止用户使用已经拥有的ticket。限制一个ticket的生命周期,就是为了防止无时间限制的滥用。
ticket中还有其它很多信息和标志,用来指示它们的行为,但是这篇文章不会讲这么多。我们将会在介绍完认证系统的工作方式以后再来介绍ticket和标志位。
1.3.4 加密
就像你所看到的 一样,Kerberos经常需要加密和解密在认证过程中的参与者之间传递的消息(ticket和authenticator)(译注:authenticator的概念后面会讲)。需要强调的是,Kerberos只使用对称加密算法(也就是说同样的密钥即用于加密也用于解密)。某些工程(比如pkinit)致力于引用公钥系统,利于与特定公钥相关的私钥来对初始用户进行验证,但是鉴于这种做法并没有一个标准,所以我们会暂时跳过。
1.3.4.1 加密类型
Kerberos 4只实现了一种加密类型,就56位DES。这种加密算法的脆弱性和一些协议上的漏洞(译注:指Kerberos 4协议上的漏洞)使得Kerberos 4已经不再使用 了。Kerbeors 5,不同于它的前任,没有它所支持的加密算法的数量和类型。而是由Kerberos的不同实现来支持和沟通各种不同的加密算法(译注:这里的沟通应该是指KDC和Client间对使用的加密算法进行沟通)。但是,这种灵活性和可扩展性却加剧了Kerberos的各种实现之间的互操作问题。Kerberos的不同实现的客户端、程序和认证服务器之间如果想要互操作(interoperate), 它们必须至少有一种相同的加密算法。Unix实现的Kerberos 5和Windows的Active Directory之间的互操作存在的因难就是一个典型的例子。事实上,Windows Active Directory只支持有限的几种加密算法,并且它和Unix的Kerberos实现间只有56位DES是相同的。这就使得如果它们想要互操作的话,56位DES必须被启用,虽然这样做的风险是众所周知的。这个问题后来被1.3版本的MIT Kerberos 5解决了。这个版本加入了对RC4-HMAC的支持,这个算法也被Windows支持,并且比DES更加安全。在Kerberos支持的算法中(但是不被Windows支持),3DES和更新的AES128和AES256值得一提(译注:这些算法的安全性都还说得过去,三者之间AES256是最强的,通常AES128已经够用,而3DES的加解密速度会比较慢。对于JDK,AES256需要下policy文件才能使用)。
1.3.4.2 密钥
在前边提到过,Kerberos的一个目标就是防止用户的密码被以未加密的形式存储,即使是在认证服务器的数据库里。考虑到不同的加密算法使用不同的密钥长度,那么,很明显的就是不能强制用户为每个加密算法指定一个符合它们长度要求的密码。因为这个原因,Kerberos引入了string2key函数,这个函数会所未加密的密码转化成符合加密算法要求的长度的密钥。这个函数在每次用户更改密码或者用密码进行验证时都会被调用。string2key被称作一个哈希函数,意思是它是不可逆的:知道密钥是不能够推出来生成它时使用的密码的(除了暴力破解)。最著名的哈希算法是MD5和CRC32.(译注:另一个著名的哈希算法是SHA系列。CRC32只能生成32位的摘要,因此通常用于做校验码。而MD5有126,SHA1有160位,理论上,消息摘要的长度越大,碰撞的可能性越小)。
1.3.4.3 盐 Salt
Kerberos5, 不同于Kerberos 4, 引用于密码盐(password salt)的概念。盐被加到密码的明文的后面,然后再通过string2key函数来获取密钥。Kerberos 5使用用户的principal作为盐:
Kpippo = string2key ( Ppippo + "[email protected]" )
这种形式的盐有以下的优点:
- 属于同一个realm的两个principal,即使有相同的密码,也会有不同的密钥。比如,如果一个管理员有一个principal用于日常工作([email protected]),另一个用于管理工作(pippo/[email protected])。很可能这个用户为了方便,给两个principal设置了相同的密码。这种形式的盐的使用确保了有关联的密钥是不同的。
- 如果一个用户有两个在不同的realm中的账户,那么一个普遍的情况就是这两个账户的密码是相同的:感谢salt的存在,这样即使一个realm中的帐户被攻破了,也不会自动异致另一个realm中的账户被攻破。
在Kerberos 5中,也可以指定不用salt,以和Kerberos 4兼容。同样,为了和AFS兼容,也可以用配置salt不用principal的完整的名字,而只使用cell的名字。
在 讨论了加密类型,string2key,salt之后,就可以检查一下下面的说法准确性了:为了在不同的Kerberos实现之间相行互操作,它们拥有相同的加密算法还不够,它们还必须有相同的string2key和salt。
另一点需要注意的是,在解释string2key和salt的时候,我们只是提了用户principal,而没有提server principal。原因很明显:一个服务,即使和验证服务器共享一个秘密,也不会使用 一个未加密的密码(谁来输入这个密码呢?)(译注:这倒不一定...), 而是一个由管理员生成的密钥,这个密钥被存储(译注:原文是memorized)在提供服务的服务器上。
1.3.4.4 密钥版本号(kvno)
当用户修改密码或者管理员为应用服务器升级密钥的时候,这个修改会被以增加计数器计数的形式记录下来。计数器的当前值 表示密钥的版本,这被称为Key Version Number, 或者简称为kvno.
1.3.4 密钥分发中心(KDC)
我们之前讲的大都是认证服务器(authentication server)。因为它是用户和服务进行身份认证时需要基础组件,因此我们现在还更深入地看一下它,但是我们不会讲解它的操作的所有细节,对这些细节的讲解是讲解Kerberos协议那个主题提一节所要做的。
在Kerberos环境中,认证服务器(Authentication Server),由于它具有分发ticket的功能,被称为密钥分发中心(Key Distribution Center),或者简称为KDC。因为它整个在一个物理的服务器上(通常就是一个进程),所以它可以从逻辑上分成三部分:数据库,认证服务器(Authentication Server)和票据分发服务器(Ticket Granting Server)。让我们简要地分别介绍一下它们。
注意:可以使得一个realm中有Master/Slave形式的冗余服务器(在MIT和Heimdal中),或者Multimaster结构(在Windows Active Directory中)。如何获得冗余并没有在Kerberos协议中指定,而是由Kerberos的实现自己确定,因此这里不进行讨论
1.3.5.1 数据库
数据库是与用户和服务有关的条目(entry)的容器。我们用principal(也就是条目的名字)来引用一个条目,尽管通常principal这个术语被用和条目混用。每个条目包括以下的信息:
- 与这个条目相关联的principal
- 密钥和相关的kvno(译注: 注意这里并不是密码,而是密码);
- 与这个principal相关联的ticket最长可用时间
- 与这个principal相关联的ticket的最长的renew时间(译注:是指这个ticket通过renew机制累计可用的时间)(只在kerberos 5中可用)
- 决定这个ticket的具体行为的属性和标志位。
- 密码过期时间
- 这个principal的过期时间,在此之后就不为这个principal分发ticket了。
为了使得窃取数据库中的密钥更加因难,Kerberos的实现们使用master key来对数据库进行加密,master key被关联在K/[email protected]这个principal上。即使是数据库的dump,备份和master KDC到salve KDC的propagation也会被用这个密钥加密,因此,如果想要重新加载它们,就必须知道这个密钥。
1.3.5.2 AS
AS是KDC是一部分,它用于回复用户初始的认证请求,当用户没有被认证时,他必须输入密码(译注:也可以使用keytab)。作为对认证请求的响应,AS分发一个特殊的ticket,被称为Ticket Granting Ticket,简称为TGT,与TGT相关联的principal是krbtgt/[email protected](译注:意思是TGT使用krbtgt/REALM@REALM这个principal的密钥来加密)。如果用户的确具有他们所声称的身份(将下来我们会看一下他们是怎么证明这个的),它们就可以使用TGT来获取其它服务的ticket,而不是重新输入密码。
1.3.5.3 TGS
KDC的TGS组件用于为拥有可用的TGT的客户端分发service ticket,它可以确保请求一个应用服务器上的资源的身份的真伪(译注:意思是,TGS可以确保访问服务的人的身份是真实的)。TGS可以被看成是一个应用服务器(application server)(因为访问它必需要使用TGT),它被用来提供给其它服务分发ticket的服务。这里不能把TGT和TGS的后缀:第一个表示ticket,第二个表示service。
1.3.6 会话密钥 Session Kery
就像我们所看到的一样,用户和服务和KDC共享一个秘密。对于用户来说,这个秘密就是从密码推导出来的密钥,对于服务来说,就是密钥(由管理员指定)。这些密钥被称为长期密钥(long term),因为在工会话程改变时,它们是不变的。但是,用户和服务间也有必要共享秘密,至少当用户和服务之间存在工作会话的时候:这个密钥在KDC分发ticket时候生成,称为Session Key。分发给服务的session key被KDC封装在ticket中(应用服务器拥有long term key,因此可以从ticket中解码出session key),分发给用户的session key被使用用户的长期密钥加密封装。Session key在认证用户的身份真伪上起了基础性的角色,我们在接下来的章节中会看到这点。
1.3.7 Authenticator
(译注:这一段对于理解Kerberos认证流程的设计原理非常非常重要)
尽管ticket中含有用户的principal信息,并且只有应用服务器可以获取和(可能)管理这些信息(因为ticket被用服务的密钥加密),但这不足以保证用户身份的真伪。一个冒名顶替者可以在一个合法的客户端向应用服务器发送ticket时捕捉(记住Kerberos的假设是在一个开放以及不安全的网络)这个ticket,并且在适当的时候发送它,来非法获取服务。另一方面,在ticket中包含可以使用这个ticket的ip地址并不是非常有用:众所周知的是在一个开放和不安全的网络环境中,网各地址可以很容易地伪造。
为了解决这个问题,我们必须利于一个事实:客户端和服务器至少在一个公话中共享会话密钥,并且只有它们知道这个密钥(KDC也知道,因为正是KDC产生了这个密钥,但是根据定义,KDC一定是可信的!!!)。因此,以下的策略被采用: 和包含ticket的请求一起,客户端添加了另一个包(就是authenticator),用户的principal和时间戳(当时的时间)被用session key加密后放在这个包里;提供服务的服务器在收到这个请求后,解开第一个包,获取session key,如果用户的确是他/她所声称,那么服务器就可以解密authenticator,并且提取时间戳。如果这个时间戳和服务器的时间相差在两分种以内(这个值 是可以配置 的),那么这个认证成功。这个时间给同一个realm中的服务器的同步划了一个底线。(译注:以上的逻辑并不足以阻止攻击者的重放攻击。攻击者可以窃听客户最终向服务器发送的信息,即加密后的ticket和authenticator,然后接下来重放这些信息给服务器。实际上,Kerberos协议有有制可以阻止这种攻击。就是服务器不仅会检查authenticator中的时间戳,还会检查它是否曾经见到过这个authenticator。以上这些也表明了Kerberos本身并不能保证客户端和服务器之间传递的消息并不会被窃听,要做到这点,需要对二者建立连接之后的通讯进行加密。)
1.3.8 Replay Cache
以下的可能性仍然存在:一个冒名顶替者同时窃取了ticket和authenticator,并且authenticator可用的2分钟内使用它。这很难,但不是不可能。为了在Kerberos 5中解决这个问题,引入了Replay Cache。应用服务器application server(也在TGS中)可以记住过去两分种内到达的authenticator,并且拒绝重复的authenticator。只有冒名顶替者没有能力在合法请求到达前复制ticket和authenticator然后发送它们到service,他的攻击就不能得逞。如果他能够做到的话,那么真正的用户就会被拒绝,而冒名顶替者将会成功获取服务
1.3.9 Credential Cache
客户端从不保存用户的密码,也不会记住 它通过string2key获得的密钥:密钥被用于解密KDC的回复并且被立即丢弃。但是,另一方面,为了实现单点登录(singl e sign-on)功能,也就是说用户在一个工作会话中只被要求输入一次密码,必须要存储ticket和相关的会话密钥。这些数据被存储的地方叫做"Credential Cache". 这个cache的位置并不由kerberos协议来指定,而是由它的实现来决定。通常为了可移值性的原因,这个cache被放在文件系统中(MIT和Heimdal)。在其它的实现中(AFS和 Active Directory),为了增加脆弱的客户端的安全性,credential cache被放在了一个只有内核 才能访问的内存区域,并且不会被交换到磁盘。