第三幕
第二天一早,Athena在咖啡间遇上了Euripides。在Euripides倒咖啡的时候,Athena拍了 拍Euripides. Athena: 我有了一个新的Charon的版本来解决我们的问题。 Euripides: 真的吗?好快呀。 Athena: 好,你看,这些问题困扰了我一夜。 Euripides: 一定是你良心发现了。我们去那边的小会议室吧? Athena: 好的。 两人去了小会议室。 Athena: 我要重新描述问题,但我要根据我们的需要进行适当的转换。 Athena清了清嗓子。 Athena: 第一个限制:用户只输一次口令,在他们工作站启动的时候,这意味着当你需要申请新的服务的票时,不需输入你的口令。第二个限制:口令不能在网络上进行明文传输。 Euripides: 好的。 Athena: 我以第一项限制开始:你只需要输入你的口令一次。我创造了一个新的网络服务来解决这个问题。 它叫做“票据授权”服务,这个服务把Charon的票给用户。使用它必须要有票:票据授权的票。 票据授权服务其实只是Charon的一个版本,它可以存取Charon的数据库。它是Charon的一部分,可以让你通过票而不是口令来进行认证。 总之,认证系统现在是象这样工作的:你登录到一个工作站,用一个叫kinit的程序与Charon 服务器通讯。你向Charon证明你的身份,kinit程序取得一张票据授权票。 现在你想从邮件服务器上取你的邮件。你还没有邮件服务器的票,所以你用“票据授权”票去取邮件服务的票。你不需要使用口令去取新的服务票。 Euripides: 每次我想要另一种网络服务的时候,我都要去取一张“票据授权”票吗? Athena: 不。记住,上次我们已经同意票是能被重用的。一旦你要用到票据授权票,直接用就可以了。 Euripides: 好,有道理。既然你能重用票,一旦你得到了某个服务的票,你就无需再去取了。 Athena: 对啊,那不好吗? Euripides: 好的,我没话说,只要你在取得票据授权票的时候没有用明文在网上传输你的口令。 Athena: 如我所说,我已解决了这个问题。听起来好像是,当我说我要和Charon联系取 得票据授权票的时候,你就要在网络上传输明文密码。但其实不是这样的。 实际上是,当你用kinit程序取得票据授权票的时候,kinit没有把你的口令送 给Charon服务器,kinit只送你的用户名。 Euripides: 很好。 Athena: Charon用用户名去查找你的口令。然后Charon就会组一个包含票据授权票的包。在送给你之前,Charon用你的口令去把这个包加密。 你的工作站收到了包。你输入你的口令。kinit用你的口令对这个包进行解密。如果成功你就向Charon成功的进行了认证。你现在有了票据授权票,你可以用这张票来取得其它的票。 这些奇思妙想怎么样? Euripides: 我不知道...我正在思考。你知道你的系统一部分工作得很好。你的系统只需要我认证一次。以后,Charon会给我服务的票而我需要关心。天衣无缝,天衣无缝。但服务票的设计还是有一些困扰我。服务票是可重用的。我同意它们应该能被重用,但重用的服务票,由于它们自身的性质,是非常危险的。 Athena: 什么意思? Euripides: 这样看。假设你正在用一个不安全的工作站。在你登入后,你需要邮件服务票,打印票,和文件服务票。假设你无意中在你退出后留下了那些票。 现在假设我登录到那个工作站并且发现了那些票。我想制造一些麻烦,于是我就用你的名字登录了。既然那些票上是你的名字,那我就可以取你的邮件,打大量的文件。这些完全是因为这些票被偶然的放在了那里。 并且我还可以把这些票拷走,永远的使用它们。 Athena: 但是这很好解决。我们可以写一个程序,在用户退出的时候把票销毁掉,这些 票也主不能再用了。 Euripides: 那么很明显你的统应该有一个票据销毁程序,让用户依赖这样的机制是非常愚蠢的。你不能指望用户在他们退出的时候会销毁票据。并且甚至不能依赖销毁票据本身,看下面的情况。 我有一个程序可以监视网络并且拷内别人的服务票据。假设我想牺牲你。我等你登到工作站的时候,打开我的程序并拷贝一份你的票。 我等你退出并离开。我把我的工作站的地址调整为你登录时用的地址。我让工作站认为我是你。我有你的票,你的用户名,你的地址。我可以用这些票来使用你的服务。 你离开工作站时销毁你的票已没并系。这些我偷来的票可以一直使用下去,因为你现在的票并没有可以使用多少次的期限,或可以使用多长的时间。 Athena: 哦,我明白你所说的了!票不能是永远合法的,因为它可能是一个非常大的安全隐患。我们应该限制每一张票可以用多长的时间,也许可以给每张票设一个有效期。 Euripides: 非常正确。我想票需要增加两项信息:生存期表示票多长时间内是合法的,和一个时间标记来说明Charon是什么时候发出这张票的。 Euripides走到了黑板写下了如下的内容: 票{用户名:地址:服务名:有效期:时间戳} Euripides: 现在当服务解开票时,它检查票的用户名,地址是否与发送者匹配,然后它 用有效期和时间戳来检查票是否有效。 Athena: 很好。典型的票使用哪长的有效期呢? Euripides: 我不知道。也许是一个典型工作站的工作周期。就八小时吧。 Athena: 那如果我在工作站呆的时间超过八小时,所有的票将会失效。包括票据授权票。那我就要重新向Charon作认证,在八小时以后。 Euripides: 是不是不合理? Athena: 我想不是。好我们就定下来吧--票在八小时后失效。现在我有一个问题问你。假设我从网络上拷了 你的票--。 Euripides: (眨了眨眼睛)啊,Tina!你不会真的这样做吧? Athena: 这只是为了讨论。我拷了你的票。现在我等你退出并离开。假设你有一个医生的约会或聚会要参加,你在两个小时后退出,并且你在退出之前销毁了你的票。 但我已经偷了你的票,它们还可以使用六小时。这给了我足够的时间用你的名义去取你的文件并打印一千份什么东西。 你看,时间戳工作的很好如果小偷选择在它失效以后来用的话。如果小偷能在它失效之前用...。 啊,好...当然,你是对的。 Athena: 我想我们遇上了一个大问题了。(她叹了口气) 停了一下。 Euripides: 我想这意味着你今晚要忙了。再来点咖啡? Athena: 为什么不。
第四幕
第二天早上在Euripides的办公室。Athena来敲门。 Euripides: 你今早有黑眼圈了。 Athena: 好了,你知道的。又是一个漫漫长夜。 Euripides: 你解决了重演的问题了吗? Athena: 我想是的。 Euripides: 请坐。 她坐下了。 Athena: 照旧,我重申一下问题。票是可重用的,在一个限定的时间内(八小时)。如果谁偷了你的票并在它失效之前使用,我们毫无办法。 Euripides: 确实如此。 Athena: 我们可以把这个问题理解为设计一种别人无法重用的票。 Euripides: 但这样的话你每次用新服务时都要取一张新票。 Athena: 对。但那是很笨的解决办法。(稍顿。)啊,我怎样继续我的讨论呢?(她沉思了一会儿)。 好的,我要重述一个问题,看有什么必须条件。网络服务必须能够证明使用票的人就是票上所申明的人。 我来顺着认证的过程再走一遍,这样我就可以演示我的解决方案。 我现在想用一个网络服务。我通过启动工作站上的客户端来使用它。客户端送三样东西给服务器:我的名字,我的工作站的网络地址,适当的服务票据。 这张票包含了申请这张票的人的名字和他(她)申请时所使用的工作站的地址。它也包含了票的有效期和时间戳。所有这些信息都被服务的密码加密了。 我们现在的认证模式基于以下的测试: 服务能对票解密吗? 票在有效期以内吗? 票中的名字和地址与申请者的名字和地址匹配吗? 这些测试证明了什么? 第一个测试证明了票是不是来自Charon.如果票不能被适当的解密,说明票不是来自真正的Charon. 真正的Charon会用服务的票来加密票。Charon和服务是唯一知道服务密码的两个实体。如果票被成功的解密,服务知道它来自于真的Charon.这个测试防止了有人伪造假票。第二项测试检查票是否在有效期以内。如果过期,服务拒绝。这项测试阻止使用旧票,因为票可能是偷来的。 第三项测试检查票的用户名和地址是否匹配请求者的用户名和地址。如果测试失败,说明使用者使用了别人的票。这张票当然被拒绝。如果名字和地址匹配,这个测试证明了什么?什么也没有。票可以被偷走,用户名和网络地址都可以被改变,如果需要的话。正如我昨天指出的那样,票可以在有效期内被盗用。 因为服务不能确定票的发送者是不是合法用户。 服务之所以无法判断是因为它没有与用户共享一个秘密。这样看。假如我正在埃尔斯诺 尔(哈姆雷特中的城堡)值勤,你打算来和我换班。但除非你说出正确的口令,否则我不 会与你换班的。我们共享了一个秘密。它可能是某人为所有值勤的人所设的。 于是昨晚我就在想,为什么Charon不能为合法用户与服务之间设一个口令呢?Charon发 一份口令给服务,同时发一份给用户。当服务从用户那里收到一张票,它可以用这个口令 检验用户的合法性。 Euripides: 等一下。Charon如何同时发两份口令? Athena: 票据的拥用者从Charon的回应中得到口令,像这个样子: 她在黑板上写下了: Charon的回应-[口令|票] 服务从票中获取口令。票的格式如下: 票-{口令:用户名:地址:服务名:有效期:时间戳} 当你要请求服务时,客户端程序生成一个‘验证器’。验证器包含了你的名字和你工作站的地址。客户端用口令把这些信息加密,口令是你请求票据时得到的。 验证器-{用户名:地址}用口令加密。 生成验证器以后,客户端把它和票一起送给服务。因为服务没有口令,所以它不能解密 验证器。口令在票中,于是服务先解开票。 解开票以后,服务得到以下的东西: 票的有效期和时间戳; 票的拥有者的名字; 票拥有者的网络地址。 口令。 服务检查票是否过期。如果一切正常,服务就用口令去解验证器。如果解密没有问题,服务将会得到一个用户名和网络地址。服务用它们去和票里的用户名和网络地址去匹配,如果正确,那么服务认为票的发送者确实是票的真实拥有者。 Athena暂停了一下,清了清喉咙,喝了点咖啡。 我认为口令验证器的机制解决了盗用的问题。 Euripides: 也许。但我想。。。攻击这个系统我必须有验证器。 Athena: 不。你必须同时拥有验证器和票。没有票,验证器是没有用的。解开验证器必须要有口令,服务必须解开票才会有口令。 Euripides: 好,我明白了,你是说当客户程序联系服务时,它同时送上票和验证器? Athena: 是的,我就是这个意思。 Euripides: 如是真是这样,什么可以阻止我把票和验证器都偷走呢?我可以写一个程序,如果我拥有了票和验证器,我就可以一直使用它至有效期结束。我只需改变我的用户名和工作站的地址。不是吗? Athena: (咬了咬她的嘴唇)是的。多沮丧啊。 Euripides: 等等,等等,等等!这不难解决。票在有效期内是可重用的,但那并不意味着验证器是可重用的。假设我们设计了验证器只可以被用一次。这可以吗? Athena: 好,也许。我样来想一下,客户端程序生成验证器,然后把它和票一起送给服务。真的票和验证器比你拷贝的要先到。如果验证器只能被用一次,你的拷贝就失效了。 啊,这就对了。我样现在需要做的就是发明一和方法使得验证器只能被用一次。 Euripides: 没问题。我们把有效期和时间戳放在上面。假设每个验证有两分钟的有效期。当你想用一个服务时客户端生成验证器,标上当前的时间,把它和票一起送给服务。 服务器收到了票和验证器,服务器解开验证器,它检查验证器的时间戳和有效期。如果验证器还没失效,所有其它的检查都通过了,那么服务器就认为你通过了认证。 假设我通过网络拷贝了一份验证器和票,我必须改变我的工作站的网络地址和我的用户名,这差不多要用几分钟。那是非常苛刻的要求,我不认为是可能的,除非。。。 嗯,有一个潜在的问题。假设不是在网络的转输中拷贝到票和验证器,我拷贝了一份原始的从Charon而来的包,这个包是你向Charon请求时的回应。 这个包,有两个口令在里面:一个是你的,一个是服务的。服务的口令隐藏在票中,我取不到,但另一个呢?那个你用来生成验证器的? 如果我得到了口令,我就用它来建自已的验证器,如果我能建自已的验证器,我就能攻破你的系统。 Athena: 这就是我昨晚所想的,但是当我顺着票的处理过程一想,发现那样偷走验证器是不可能的。 你在一台工作站坐下,用kinit程序得到你的票据授权票。kinit要求输入用户名,你输入以后,kinit把它送给Charon.Charon用你的名字查找你的口令,然后生成一张票据授权票。作为处理的一部分,Charon生成了一个你与票据授权服务共享的口令。Charon把口令和票据授权票一起送给你,并且在发关之前用你的口令将它加密。 Charon送出了包。某人取得了这个包,但他们无能为力因为它是用你的口令加过密的。特别是,无人可以偷走票据授权服务的口令。 kinit收到了票据包并要求你输入你的口令。如果你输入正确的口令,kinit解开包取出了口令。 现在你注意kinit的处理,你去取你的邮件。你打开邮件客户端。这个程序查找一张邮件服务的票但没有找到(你还没取过你的邮件)。客户端用票据授权票去申请一张邮件服务的票。 客户端为票据授权的过程生成了一个验证器,并用票据授权的口令把验证器加密。客户端把验证器送给了Charon,票据授权票,你的名字,你的工作站的地址,邮件服务的名字。票据授权服务收到了这些东西,并通过了认证检查。如果一切都通过,票据授权服务将会得到那个与你共享的口令。现在票据授权服务为你生成了一张邮件服务的票,在这个过程中生成了一个你与邮件服务共享的口令。票据授权服务把这些东西打成包送给你的工作站。包里有票和口令。在送包之前,票据授权服务用票据授权的口令把包加密。做完以后,包被送出去。 这样邮件服务票的包通过网络被送了出来。假设网络上的某人将它复制了一份。他不幸的发现包是用票据认证的口令加过密的。既然无法解密,他就不能得到邮件口令。没有口令,他就不能使用任何在网络上传送的邮件服务的票。 现在我觉得我们是安全的。你认为呢? Euripides: 也许吧。 Athena: 也许!你就只会说这个吗! Euripides: (大笑)别在意。你现在应该知道我处理问题的方式了。我猜我和你昨晚都工 作到了半夜。 Athena: 哼! Euripides: 好的,大半夜。实际上,这个系统似乎是完全可行的。口令的方案解决了我 昨晚想到的一个问题:相互验证的问题。 稍顿。 我说一下好吗? Athena: (有点冷淡)请便。 Euripides: 你真好。(Euripides清了清自已的嗓子)昨晚,当口令和验证器在我脑子里转的时候,我想去找出这个系统新的问题,我想我发现了一个很严重的问题。我下面就演示一下。 假设你厌倦了现在的工作,决定换一个。你想用公司的激光打印机打印求职信,把它们送给猎头和其它的雇主。于是你输入打印命令,命令去取得服务票,然后把票送到打印机。这是你认为它应该被送到的地方。实际上你并不知道你的请求被送到了正确的打印服务器。 假设一些无耻的人--比如说你的老板--调整了系统,把你的请求送到了他办公室的打印机。他的打印服务不关心票的内容。它告诉你的工作站服务已准备好打印你的文件。打印命令被送到了假的打印服务器,你有麻烦了。 我从相反的方向表达了相同的问题。用口令和验证器,Charon能够保护的它的服务器防止错误的用户使用,但它不能保护它的用户使用错误的服务器。系统需要为客户端程序提供一种验证服务器的方法,在它向服务器发送敏感信息之前。系统必须允许交互验证。 但口令的方案解决了这个问题。让我们回到打印服务器的场景。我想要打印客户程序确 认它送交的服务是合法的服务。 这就是程序要做的。我输入打印命令并给出一个文件名。这时我已经有了打印服务票和口令。客户程序用密码生成了一个验证器,然后把验证器和票送给了假设的打印服务器。客户端这时还没有送打印文件,它在等待从服务的返回。 真的服务收到票和验证器,把票解密并得到口令,然后用口令解开验证器。这样服务端做完了所有的认证。 测试已经确认了我的身份。现在服务程序要准备一个响应包来证实它自已的身份。它用口令加密了返回包,并把包送给了等待的客户端。 客户端收到了包并试图用口令把它解开。如果包被正确的解开得到了正确的服务器响应信息,客户端程序就知道了这个服务器是合法的服务器。然后这时客户端向它发出打印命令。 假设我的老板改变了一下系统使得他的打印机看起来好像是我想要用的那个。我的客户端送了票和验证器给它并等待它的响应。假冒的打印服务无法生成正确的响应因为它无法把票解开并得到口令。这样的话客户端就不会送打印命令给它因为客户端没有得到正确的 响应。最后客户端放弃等待并退出。我的打印没有完成,但至少我的求职信不会放在我的 对头的桌子上。 好啊,我想我们有了Charon认证系统的坚实的基础。 Athena: 也许。不管怎么说,我不喜欢Charon这个名字。 Euripides: 你不喜欢吗?什么时候? Athena: 我从来都不喜欢,因为它的名字听起来没意义。有一天我和我荷迪斯(冥王)叔叔 谈到了这个,他推荐了另一个名字:冥王的三个头的看门狗。 Euripides: 啊,你是说“Cerberus". Athena: 你说什么语言啊!"Cerberus"实际上是。。。 Euripides: 哦,不叫这个吗? Athena: 当然,谁让你是罗马人!而我是希腊人,它是一条希腊的看门狗,它的名字是” Kerberos“,”Kerberos“是‘K’打头的。 Euripides: 好吧,好吧,别发火。我同意这个名字。实际上,它有一个好的脖环。再见 吧,Charon,欢迎你,Kerberos. 后记 这篇对话是于1988年写的,是为了帮助读者理解Kerberos V4的运行方式。经过了这么多年,它仍然非常好的服务于此。当我把这篇文章转换成HTML的时候,我惊讶的发现这个文档对Kerberos V5仍然非常有用。虽然很多东西改变了,但核心概念并没有变。实际上,Kerberos V5对Kerberos只做了两处改变。 第一处改变是因为意识到验证器用少于五分钟的有效期不足以防止攻击者进行重演,如果攻击者是用一个程序自动的截取票和验证器并进行重演的话。 在Kerberos V5中,验证器真正的只能用一次因为服务器用‘重演缓冲区’保存了最近一次提交的验证器的信息。如果攻击者试图截取验证器并重用它,‘重演缓冲区’会发现验证器已经被提交了。 第二个主要改变是Kerberos送给kinit服务票的时候,票不再是用用户的口令加密。它已经用票据授权服务的口令加过密了。票据授权服务的票被用来获取其它票的时候,它直接就被传输了。因此票不需要再用用户的口令加密一次。(服务器响应的其它部分,如口令,仍然是用用户的口令加密的。) 一个类似的改变也应用到票据授权服务协议;从票据授权服务返回的票也不再用票据授 权服务的口令来加密了,因为它所包含的票已经被对应的服务的口令加过密了。举例来说, Kerberos V4的包像这样: KDC_REPLY = {TICKET, client, server, K_session}K_user 意思是:{}中的内容是用K_user来加密的。 TICKET = {client, server, start_time, lifetime, K_session}K_server 在Kerberos V5中,KDC_REPLY现在看起来像这样: KDC_REPLY = TICKET, {client, server, K_session}K_user (注意:票已经不再用K_user来加密了) 当然,Kerberos V5中还有许多新特性。用户可以在另一个网络中安全的提交他们的票;并且,用户可以把他们的一部分认证权转给服务器,这样服务器就可以作为用户的代理。其它的新特性包括:用更好的加密算法替换了DES加密算法,如三重DES加密。读者如果对V4与V5的变化感兴趣的话,可以读一下"The Evolution of the Kerberos Authentication System",作者是Cliff Neumann和Theodore Tso. 我希望你能对这篇介绍Kerberos协议的文章感兴趣。我祝愿你在未来的探索中更进一步。 |