Kerberos认证解析
kerberos定义
- Kerberos 是一种网络认证协议,其设计目标是通过密钥系统为客户机、服务器应用程序提供强大的认证服务。
- Kerberos 作为一种可信任的第三方认证服务,是通过传统的密码技术(如:共享密钥)执行认证服务的。
- Kerberos也能达到单点登录的效果,即当Client通过了Kerberos server的认证后,便可以访问多个Real Server。
kerberos几个重要组件
- KDC又称Kerberos服务器:负责管理发放Ticket和记录授权的中心服务器,包含两部分:AS认证服务器(Authentication Server),TGS服务授权服务器(Ticket Granting Server)
- Client 客户端:需要使用kerbores服务的客户端
- AppServer 服务端:提供具体服务的服务端
- Client在Kerberos注册用户名c,密码Kc;Kerberos服务器将用户的用户名及密码存放在它的数据库中,因此密码Kc只有Client和Kerberos才拥有。
- AppServer在Kerberos注册用户名s,密码Ks;同理密码Ks只有AppServer和Kerberos才拥有。
- TGS在Kerberos注册用户名tgs,密码Ktgs;同理密码Ktgs只有TGS和Kerberos才拥有。
kerberos中几个重要概念
- 用户principal的命名:类似elis/[email protected],形式是用户名/角色/realm域。
- 服务principal的命名类似ftp/[email protected],形式是服务名/地址(提供者)/realm域;
- principal(认证的主体即简称“用户名”):每添加一个服务或者用户就要添加一条principal,每个principal都有一个密码。用户principal的密码用户自己记住,服务的principal密码服务自己记录在硬盘上(keytab文件中);
- realm(Kerberos的域):有点像编程语言中的namespace。在编程语言中,变量名只有在某个"namespace"里才有意义。同样的,一个principal只有在某个realm下才有意义。所以realm可以看成是principal的一个"容器"或者"空间"。相对应的,principal的命名规则是"what_name_you_like@realm"。
在kerberos, 大家都约定成俗用大写来命名realm, 比如"EXAMPLE.COM"
- Kerberos的进程:有krb5kdc(主进程,也就是KDC)、kadmind(用于远程管理principal数据库)、kpropd(slave同步数据用)。配置文件在/etc/krb5.conf和/var/kerberos/krb5kdc/下,数据库在/var/kerberos/krb5kdc/princical;
- Kerberos只提供一种功能——在网络上安全的完成用户的身份验证。它并不提供授权功能或者审计功能。
kerberos认证交互过程
kerberos认证交互过程可以简要概括为以下图片中的三次通信
- 每次通信,消息包含两部分,一部分可解码,一部分不可解码;
- 服务端不会直接与KDC通信
- KDC保存所有机器的账户名和密码
- KDC本身具有一个密码
第一次通信
client-->AS(Authentication Service)认证服务
- 想要访问http服务,首先要向KDC表名自己的身份,通过未加密的信息发送至KDC获取Ticket Granting Ticket (TGT)。
- 信息包含(你的用户名/ID、你的IP地址、TGT的有效时间);
- Authentication Server收到你的请求后,会去数据库中验证,你是否存在。注意!仅仅是验证是否存在,不会验证对错。
- 如果存在,Authentication Server会产生一个随机的Session key(可以是一个64位的字符串),这个key用于你和Ticket Granting Server (TGS)之间通信。Authentication Server会发送两部分信息给你,一部分信息为TGT,通过KDC自己的密码进行加密,包含(你的name/ID、TGS的name/ID、时间戳、你的IP地址、TGT的生命周期、TGS session key),另外一部分通过你的密码进行加密,包含的信息(TGS的name/ID、时间戳、生命周期、TGS session key)
- 如果不存在会报出:client not found in Kerberos databases while getting initial这样的错误,意思就是KDC的数据库中没有找到你这用户。
- 使用你的密码解密第二部分信息,获取到TGS session key。如果密码不正确,无法解密,则认证失败。第一部分信息TGT,你是无法解密的,但需要暂时缓存起来。
第二次通信
client-->TGS(Ticket Granting Service)票据授予服务器
如果第一步认证成功了,说明此时你已经拥有了一个无法解密的TGT和一个通过自己密码解密第二部分信息获取到的TGS Session Key。
- 请求信息包含三部分:1.通过TGS Session Key加密的认证器部分(你的name/ID、时间戳)2.明文传输部分:(请求的Http服务名(就是请求信息)、HTTP Service的Ticket生命周期)3.TGT部分
- Ticket Granting Server收到信息后,首先检查数据库中是否包含有你请求的Http服务名。
- 如果无,直接返回错误信息。
- 如果存在,则通过KDC的密码解密TGT,这个时候。我们就能获取到TGS Session key。然后,通过TGS Session key去解密你传输的第一部分认证器,获取到你的用户名和时间戳。
- TGS再次进行验证:
- 对比TGT中的用户名与认证器中的用户名
- 比较时间戳(认证器中的时间戳和系统的时间戳),不能超过一定范围(通常30分钟),时间戳用于防止重放攻击,所以所有的机器都必需使用安全的类似NTP的机制把时间同步好。
- 检查是否过期
- 检查IP地址是否一致
- 检查认证器是否已在TGS缓存中(避免应答攻击)
- 验证通过后TGS会发送两部分信息给你:1.通过Http服务的密码进行加密的信息(ST):(你的name/ID、Http服务name/ID、你的IP地址、时间戳、ST的生命周期、Http Service Session Key)和 通过TGS Session Key加密的信息(Http服务name/ID、时间戳、ST的生命周期、Http Service Session Key)
- 你收到信息后,通过TGS Session Key解密,获取到了Http Service Session Key,但是你无法解密ST。
第三次通信
client-->Http服务
前面两步成功后,以后每次获取Http服务,在Ticket没有过期,或者无更新的情况下,都可直接进行这一步。省略前面两个步骤。
- 向Http Server发送请求信息,包含两部分:1.通过Http Service Session Key,加密部分(你的name/ID、时间戳),2.ST
- Http服务端通过自己的密码解密ST(KDC是用Http服务的密码加密的),这样就能够获取到Http Service Session Key,解密第一部分。
- 服务端解密ST后,进行检查
- 对比ST中的用户名(KDC给的)与认证器中的用户名
- 比较时间戳(认证器中的时间戳和系统的时间戳),不能超过一定范围
- 检查是否过期
- 检查IP地址是否一致
- 检查认证器是否已在HTTP服务端的缓存中(避免应答攻击)
- Http Server应答信息:通过Http Service Session Key加密的信息(Http服务name/ID、时间戳)
- 你通过缓存的Http Service Session Key解密这部分信息,然后验证是否是你想要的服务器发送给你的信息。完成你的服务器的验证。
至此,整个过程全部完成。
kerberos疑问
- 上面有个数据包是KDC经Client转发给Server的,为什么不直接发给Server?
- 因为Server可能给多个Client提供服务,这样Server需要维护一个Client和会话密钥的对应表,这对Server是一个负担。
- 为什么要发两份关于Client的信息给Server?
- 通过这两份数据的对比,Server就能判断出是不是对的Client在访问服务。
- Client是如何判断自己在访问对的Server呢?
- 因为Client给Server的一个数据包是用Server的master key来加密的所以只有对的Server才能解密。
- 为什么要用会话密钥
- 通信方的master key是长期有效的,如果在网络上传输,一旦被截取,理论上来说只要有足够的时间是可以破解的。
- 所以我们才用临时的会话密钥来通信,一段时间后会话密钥会过期,同时时间戳也防止了,恶意用户重复使用同一个数据包。
- 为什么要用时间戳?
- 如果Client向Server传送的数据包被其他的Client截取,然后自己拿来向Server请求服务这,这样就会出问题
- Server收到请求后将从解密后的数据包中获得的时间戳和当前时间对比,一旦超过一定范围将直接拒绝请求
kerberos自身的安全工作如何做?
- kerberos的安全是建立在主机都是安全的而网络不是安全的假定之上的。所以kerberos的安全其实就在于把主机的安全做好;
- 尤其是KDC那台机器的安全。出于安全的考虑,跑KDC的机器上不能再跑别的服务,如果KDC被攻陷了,那么所有的密码就全部泄露了;
- 如果只是服务的机器被攻陷了,那么更改服务的principal的密码即可;
- 如果是用户的机器被攻陷了,那么在ticket超时(一般是数小时的时间里)之前,用户都是不安全的,攻击者还有可能尝试反向用户的密码;
- Kerberos依赖其它的服务来存放用户信息(登录shell、UID、GID啥的),因此需要注意到这些信息依然是很容易遭受攻击并且泄露的;
- 由于任何人都可以向KDC请求任何用户的TGT(使用用户密码加密的session key),那么攻击者就有可能请求一个这样的包下来尝试解密,他们有充足的时间离线去做这个工作,一旦解开了,他们也就拿到了用户的密码。简单密码几乎一解就开,所以不能设置简单密码,也不能在字典里。另外还可以打开Kerberos的预验证机制来防御这种攻击,预验证机制就是在KDC收到用户请求TGT的请求之后,要求用户先发一个用自己密码加密的时间戳过来给KDC,KDC如果确实可以用自己存储的用户密码解密,才发TGT给用户,这样攻击者在没有用户密码的时候就拿不到可用于反向的包含用户密码的TGT包了。在MIT kerberos中在配置文件中default_principal_flags = + preauth可以打开这个机制。但这个机制也并不是无懈可击的,攻击者依然可以通过嗅探的方式在正常用户请求TGT时拿到上述的那个包(这个难度显然就高了一些);
- 还有一个问题是攻击者可能伪造一个KDC,然后用一个伪造的实际不存在的用户向这个KDC请求验证,通过后他就得到了一个用户登录系统的shell。这种攻击需要在客户端防御,需要客户端主动去验证一下KDC是否是正确的KDC。具体来说就是客户端在得到TGT后进一步要求KDC给一个本物理机的principal(也就是一个用物理机密钥加密的串),然后尝试用物理机存储的密码去解密,由于伪造的KDC没有物理机(host/hostname)的principal密码,所以它无法给出这个包,也就被客户端认定为是伪造的KDC,认证失败。这个机制需要在客户端开启(认证服务器端么),默认是关闭的,在krb5.conf里[appdefaults]章节里pam的部分中设置validate=true来开启;
Kerberos可以在不同的realm之间建立信任关系
这样用户在一个realm中登录后就可以享用多个realm中的服务。简单的建立互信的方法是在2个域名都建立一条共享密码的名为krbtgt的principal。更好的建立互信的方法是多个realm都建立一条信任到自己的父realm,这样在子realm之间也能建立起信任;
Kerberos还能向SSL帮助网络流量套上一层加密
比如说telnet这种明文协议就可能能用上。但是跟SSL或者SSH比起来安全性仍然略低,因为它的session key是用对称加密传输的(如果TGT被盗取了,流量的加密也就失效了),这在理论上增加了被嗅探破解的可能。而SSH和SSL协商session key采用的DH算法是理论安全的(被称为PFS即Perfect forward secrecy,详见http://en.wikipedia.org/wiki/..._forward_secrecy)。还有一个问题是Kerberos采用的一些加密算法是已经不安全的了(如CRC32和DES),而另一些加密算法(AES)并没有被其他的kerberos实现广泛使用,所以会有一些兼容性的麻烦,选择加密算法时要避开这些算法;
如果将princical密码设置为random
那么每次将其dump到本地keytab中时,都会random新的密码并dump到本地(可用于更新密码);