【IT168 技术】在希腊神话中,冥界的大门由一头烈犬看守。此犬长着三个头,兢兢业业地守在冥河边,从没有灵魂能在它醒着的时候逃离。这三头烈犬就是Kerberbos,安全守卫的象征。古希腊人在下葬时要放蜜饼讨好它;现代游戏里也有它的英姿,比如《英雄无敌》里以一敌多的地狱犬。
本文要介绍的认证协议,就借用了Kerberos的名字。Kerberos是CIFS的安全基础,所以也是NAS技术人员必须掌握的协议。Kerberos的基本原理是对称加密,相互确认。服务器确认客户端身份是为了拒绝匿名或者假冒用户,用户端为何要确认服务器身份呢?举一个例子:如果你老板伪造了一台网络打印机,但是你没法确认它,就可能把求职信打到他办公室里去,然后就不得不出去求职了。相互确认又是怎么做到的呢?假设甲和乙都拥有一把相同的密钥K,并且他们都确信世界上没有第三个人拥有这把密钥,那就可以得到下面两个推论:
甲用该密钥把一段明文信息加密后,如果有人能解密出这段明文,则甲可以认定对方为乙。
乙收到加密信息后,如果能用和甲共享的密钥解密,则乙可以认定对方为甲。
这个机制的优势在于,只要传输加密的信息就可以互相认证,避免了密钥在传输时被劫持。但有个小缺陷,如果有人劫持了加密信息,就可以伪装成甲去骗取乙的信任。这就是Replay Attack(重演攻击)。Kerberos需要一套机制来防止重演攻击,这会使整个协议看起来更复杂一点。
简单采用一对一的相互认证,人太多就不好管理。比如甲乙丙丁需要互相认证,那他们需要分别保管3把密钥(如下图)。如果人数再多点,总密钥数就膨胀了。有没有办法解决这个问题呢?假如存在一个大家都信赖的权威机构,它能和所有人互相认证。这样大家只要分别保管和该机构共享的那把密钥就行了,需要验证其他人的时候就咨询该机构。比如甲需要访问丙的时候,先和该机构互相认证,再通过它认证丙。丙也可以通过该机构认证甲。下图展示了这两种不同的方式的差别。
Kerberos的整个架构比较复杂,但是基本精神就是上面的两个推论和第三方认证。如果接下来的内容令你感到困惑,建议回头看看这个基本精神,相信很多问题可以迎刃而解。
我们的域里有一台Windows客户机,一台KDC(即权威第三方),还有一台NAS。现在我们来看看用户甲从和KDC互相认证到访问NAS的整个过程。
一. 用户甲与KDC相互认证:
1. 用户甲在客户机的登陆界面上输入用户名,密码和域名。客户机上有个叫LSA的服务将处理这些输入信息。
2. LSA利用hash函数把用户密码转化为密钥Kclt,再用Kclt来加密即将发给KDC的信息,这些加密的信息称为Authenticator。随着Authenticator一起被发送的还有一些明文信息。LSA发送给KDC的这些信息就是Kerberos的身份认证请求,称为KRB_AS_REQ。
Authenticator:{用户甲相关信息,时间戳}Kclt
Note: 这表示大括号里的内容被Kclt加密后成为Authenticator,本文将一直用这种格式来表示。
KRB_AS_REQ:“我是用户甲”,Authenticator
Note: 这表示KRB_AS_REQ由明文“我是用户甲”和Authenticator组成,本文将一直用这种格式来表示。
3. KDC收到KRB_AS_REQ之后,只能读出明文部分“我是用户甲”。于是便从数据库中找出甲的密码,用同样的hash函数转化为Kclt。然后KDC就可以用Kclt解开Authenticator,得到{用户甲相关信息,时间戳}。如果用户甲的相关信息确认无误,则可认为这请求是甲生成的(推论2)。如果时间戳在规定时间内,则可排除重演攻击,因为难以在这么短时间里截获KRB_AS_REQ并在另一台客户机伪装起来。
4. KDC认证了甲的身份后,本来可以回复“{时间戳}Kclt”来完成相互认证(时间戳是从Authenticator中解密出来的,可以证明KDC有解密的能力)。但这样做不够完美,因为接下来用户甲在请求KDC做第三方认证时,信息还得用Kclt加密。用户每用一次Kclt,KDC就得从数据库取一次密码来解密,这对KDC来说是一个不小的负担。为了解决这个问题,KDC先随机生成两把一样的Kclt-kdc,作为以后用户甲和KDC共享的密钥。其中一把直接交给了用户甲,另一把KDC需要自己使用,但是又不想保管它。所以KDC利用自己的密钥Kkdc把它加密起来,成为一个只有KDC才能打开的ticket,然后一并交给用户甲保管。这个ticket叫Ticket-Granting Ticket(TGT)。KDC给用户甲回复的这些信息就是KRB_AS_REP。
TGT:{用户甲相关信息,Kclt-kdc}Kkdc
KRB_AS_REP:{Kclt-kdc,时间戳}Kclt,TGT
5. LSA收到KRB_AS_REP之后,先用Kclt解开第一部分,得到Kclt-kdc和时间戳。由时间戳可知KDC有解密能力,从而确认KDC身份(推论1)。至此用户甲和KDC终于相互认证了。接下来LSA和KDC都摧毁Kclt,因为不再需要它了。
在上面这个过程中,客户机只是辅助用户甲和KDC相互认证,它自身并没有被认证。KDC在认证结束后,仍然只保管Kkdc,没有增加负担。用户甲则获得了Kclt-kdc和TGT,相当于重复向KDC请求认证的能力。
二. 用户甲登陆到客户机,这个过程和接下来用户甲访问NAS很相似,不作详述。
三. 用户甲访问NAS:
1. 用户甲向KDC请求访问NAS的Ticket。这个请求在Kerberos里被称为KRB_TGS_REQ。
KRB_TGS_REQ:TGT,{用户甲相关信息,时间戳}Kclt-kdc,“NAS相关信息”
2. KDC收到KRB_TGS_REQ后,先用Kkdc解开TGT,得到Kclt-kdc。再用Kclt-kdc解开加了密的第二部分,得到用户甲相关信息和时间戳,由此确定这不是假冒请求或者重演攻击。接下来KDC随机生成两把一样的Kclt-nas,一把用Kclt-kdc加密,另一把用Knas加密成为Ticket,一并发给用户甲。这个Ticket和Kclt-nas将用于用户甲与NAS的相互认证。这就是Kerberos里的KRB_TGS_REP。
Ticket:{用户甲的组信息,Kclt-nas}Knas
Note:因为Ticket可以重复使用,所以一旦生成后,用户甲属于哪个组就确定了。如果在域里更改了甲的组设置,需要让用户甲重新登陆一次才能生效。
KRB_TGS_REP:{Kclt-nas}Kclt-kdc,Ticket
3. 用户甲收到KRB_TGS_REP之后,先用Kclt-kdc解密第一部分得到Kclt-nas。然后用它来建立Authenticator。最后把Authenticator和Ticket一起组成KRB_AP_REQ发给NAS。
Authenticator: {用户甲相关信息,时间戳}Kclt-nas
KRB_AP_REQ: Authenticator,Ticket
4. NAS收到KRB_AP_REQ之后,先用Knas解密Ticket,得到了Kclt-nas和甲的组信息。再用Kclt-nas解密Authenticator。如果这些信息没有问题,那NAS就可以允许甲的访问,即回复KRB_AP_REP给用户甲。
KRB_AP_REP:{时间戳}Kclt-nas
5. 用户甲收到时间戳后,即可确认这台NAS的身份(因为只有NAS有能力解密得到时间戳)。
在这个过程中,其实还是有重演攻击的可能。比如复制整个KRB_AP_REQ,然后伪装成用户甲去访问NAS。对此问题,Kerberos除了利用时间戳来限制伪造时间,还利用cache功能确保每个authenticator都只能使用一次。这就大大降低了被攻击的可能。
如果上面的过程太琐碎,看看右边的图片也许有助于理解和总结。我个人的学习习惯是什么协议都要抓个包来研究。下图展示了用户linp1在登陆10.32.106.116时,和KDC 10.32.106.103的通信过程。被选定的包是KRB_AS_REP,可以从中看到(Ticket-Granting)Ticket。