kerberos认证_Kerberos认证思想

最近部署Apache Griffin,在Griffin与CDH之间的kerberos认证上折腾了不少时间。深感对kerberos掌握不足,认真研读了网上一些文章,对kerberos认证思想有以下总结。

Kerberos 只管认证(判断客户端是否合法),鉴权由各服务自己管理。

认证这事,如果服务端只有一个,好做,客户端把用户密码发给服务端进行认证,认证成功后返回token。如果服务端部署到多台机器,就会出现单点登录问题。解决办法是把token放到分布式缓存里,各机器都访问分布式缓存。如果服务端是多个服务,且每个服务都可能对应着多台机器,首先能想到的是,认证环节需要一个专门的服务(在kerberos里是KDC(key distribution center )的AS (Authentication Server))。第二步是所有机器都连接到一个分布式缓存里,这样服务端可以取客户端的token和缓存里的token做校验。显然,第二步的实现成本很大:且不说,连接分布式缓存的过程可能发现的错误,单是要求每个机器的每个服务连接到分布式缓存且实现相同的校验逻辑,就非常难做到。

如何解决"多服务多机器"的认证问题呢?Kerberos提供了一种解决方案, Kerberos就是解决"多服务多机器"的认证问题的一种网络认证协议。

客户端注册

认证是判断客户端是否合法。要进行认证,首先得定义什么是合法。客户端是否合法的标准是 客户端是否在认证服务里注册。kerberos的认证服务叫KDC(key distribution center )。

如果我们自己实现KDC的话,第一个想法在注册阶段,是把客户端的用户名密码存储到DB。认证阶段,把客户端传过来的用户名密码和DB里的用户名密码做对比。一致返回成功,否则返回失败。KDC的做法不太一样:KDC没有存储用户密码,而是存储"用户密码"用不可逆算法加密算出来的passwordHash(密码hash)。

客户端在KDC注册时,不需要传用户密码,而是传输passwordHash。注册的伪码如下:

#客户端生成passwordHash(密码hash)

为什么不直接传用户密码而改为传密码hash?

第一,传密码有被网络拦截的风险。

第二,自己的密码不应该被别人知道,哪怕对方是认证服务。(有可能有N套认证系统,接一套认证系统给一次密码,听起来也不靠谱)。

第三,用户密码与passwordHash(密码hash)之间隔着"不可逆的hash算法",意味着可以在不改用户密码的情况下,动态调用不同的"不可逆的hash算法"生成不同的passwordHash(密码hash)。

认证流程

kerberos认证流程可以划分为三个阶段:领证,取票,验票。

69a43225e8d9b41c338621150b4452cc.png

领证阶段

为什么需要领证阶段?这好比我们自己实现认证程序里面的token。虽然上面已经通过不可逆的hash算法生成了"密码hash",但如果客户端与KDC之间的通信一直用"用户名+密码hash"来认证,网络拦截到了"用户名+密码hash",不可逆的hash算法实现得没那么好,有可能会出现密码被破译。为了减少这种风险,就会使用token机制。kerberos对应的术语叫TGT(Ticket Granting Ticket)。KDC生成TGT的伪码如下:

#KDC生成TGT的伪码
getTgt

根据以上伪码,我们发现几个有意思的点:

1. 是领证,而不是认证。客户端传入的参数只有客户端信息(简化为客户端名称name),没有客户端的"密码hash"。而且,在处理过程中,只进行"可逆编码"操作,而没有校验用户名密码操作。意味着,只要客户端请求,即可返回token。

有人疑问:KDC不校验,假如客户端B,冒充客户端A,调用getTgt(clientA)的token怎么办?

答案:客户端B可以拿到客户端A的token,但客户端B没有客户端A的"密码hash",因此解不开clientToken,得不到客户端与KDC 之间的 会话id。这一点意味着,网络拦截token不会对安全认证产生影响。

2.KDC没有存储token。完美地绕开访问token存储会遇到的一系列问题。此外,对于token的使用和处理完全交给Client。

3.没有暴露关键信息。客户端只传入客户端信息(简化为客户端名称name),降低了暴露"密码hash"的风险。返回的clientToken、tgtToken和会话有关,一定时间内会失效。

客户端领证后,会把证书缓存起来,多次使用。客户端领证伪码如下:

(){
      

取票阶段

取票是指,获取某个服务的票据(Ticket)。客户端取票的伪码如下:

# 客户端领证 --- 详看 "领证阶段"客户端的伪码
     

KDC的TSG(Ticket Granting Server)生成某个服务票据(Ticket)的伪码如下:

(clientAuthenticate , tgtToken 

对于KDC而言,所有KDC以外的服务,都可以称为KDC的客户端,包括客户端希望连接的服务端。

注意:取票阶段,KDC除了拿自己的kdcPasswordHash(密码hash)解密TGT信息,其他信息皆来源于客户端。

验票

客户端向服务端发送认证信息 ,服务端的验票的伪码如下:

boolean validate(clientAuthenticate ,serverTicket){
     # 服务端取出自身的"密码hash"
    serverPasswordHash = .....
    {name,clientToKdcSessionId,serverTickWorkTimestamp}  = reversible_decrypt(serverPasswordHash ,serverTicket)
     # 票据超过一定时长失效
     if(System.currentTimeMillis()   > serverTickWorkTimestamp ){ 
        return false;
    }
 
    { clientName , serverName } = reversible_decrypt(clientToKdcSessionId ,clientAuthenticate)    
     if(name == clientName){
        return true ;
    }
 
    return false;
}

注意:验票阶段,服务端除了拿自己的serverPasswordHash(密码hash)解密票据(ticket),其他信息皆来源于客户端。

小结

大家会发现,以上的伪码干的活基本上是用不同的passwordHash(密码hash)对关键信息进行加密解密。

"领证阶段"是获取sessionId和TGT信息。sessionId的作用是,让客户端不再使用clientPasswordHash(客户端密码hash)与其他节点交互,转用sessionId加密的"客户端的认证信息",万一,"客户端的认证信息"被拦截了,反推得到sessionId,也仅是在一小段时间内有效(比如说5分钟)。若反推时间大于sessionId的有效时间,那反推出来的sessionId也没有意义。

TGT信息的作用是,客户端每次向KDC的TSG(Ticket Granting Server)获取某个服务的票据(Ticket)时使用。TGT信息是使用KDC的kdcPasswordHash(kdc密码hash加密),只有KDC的服务才能解密。

"取票阶段"是拿sessionId加密过的客户端信息和TGT信息获取“用某个服务serverPasswordHash加密客户端信息生成”的专属某个服务的票据(serverTicket)。

"验票阶段"是先解密sessionId加密过的客户端信息,再解密专属某个服务的票据(serverTicket)得到的客户端信息,两者对比校验。

kerberos的实现比本文的伪码复杂得多,而且涉及到大量的概念和术语。本文的着力点在于,让kerberos的小白同学知道kerberos的认证大概是怎么回事。对于熟悉kerberos的朋友,文中有多次不够严谨的地方,希望你们的指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值