当前位置: 首页 > 知识库问答 >
问题:

JavaGSS-API服务票证未使用Java保存在凭据缓存中

方鸿振
2023-03-14

我已经使用GSS-API创建了2个演示Kerberos客户端。一个在Python3中,第二个在Java中。这两个客户端似乎大体上是等效的,并且都“工作”,因为我得到了一个服务票据,该票据被我的Java GSS-API服务主体所接受。

然而,在测试中,我注意到Python客户端将服务票据保存在kerberos凭据缓存中,而Java客户端似乎没有保存票据。

我使用“klist”查看凭证缓存的内容。

我的客户机运行在Lubuntu 17.04虚拟机上,使用FreeIPA作为Kerberos环境。我正在使用OpenJDK 8 u131。

问题1:Java GSS-API是否未将服务票据保存到凭据缓存?或者我可以改变我的代码吗?

问题2:服务票证没有保存到缓存有什么缺点吗?

我的假设是缓存的服务票证减少了与KDC的交互,但是关于如何使用WindowsJava客户端保存Kerberos服务票证?的评论表明情况并非如此,但此Microsoft技术说明说“客户端每次想要访问此特定服务器时都不需要返回KDC”。

问题3:python客户机缓存的服务票据在几分钟后消失——比到期日期早得多。是什么导致它们消失?

Python代码

#!/usr/bin/python3.5

import gssapi
from io import BytesIO

server_name = 'HTTP/app-srv.acme.com@ACME.COM'
service_name = gssapi.Name(server_name)

client_ctx = gssapi.SecurityContext(name=service_name, usage='initiate')
initial_client_token = client_ctx.step()

Java代码

System.setProperty("java.security.krb5.conf","/etc/krb5.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");

GSSManager manager = GSSManager.getInstance();
GSSName clientName;
GSSContext context = null;

//try catch removed for brevity
GSSName serverName = 
      manager.createName("HTTP/app-srv.acme.com@ACME.COM", null);

Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");

//use default credentials
context = manager.createContext(serverName,
    krb5Oid,
    null,
    GSSContext.DEFAULT_LIFETIME);

context.requestMutualAuth(false);
context.requestConf(false);
context.requestInteg(true);

byte[] token = new byte[0];         
token = context.initSecContext(token, 0, token.length);

编辑:

虽然最初的问题集中在使用Java GSS-API构建Java Kerberos客户端,但GSS不是必须的。我对其他适用于Java的Kerberos方法持开放态度。现在我正在用Apache Kerby kerb客户端进行实验。

到目前为止,Java GSS-API似乎有两个问题:

1) 它使用凭证缓存来获取TGT(正常),但不缓存服务票据(不正常)。

2) 它无法访问KEYRING类型的凭据缓存。(通过行为、调试Java运行时安全类以及代码中的注释确认。对于Lubuntu/FreeIPA组合,我使用的KEYRING是现成的默认值。这不适用于Windows,也可能不适用于其他Linux Kerberos组合。

共有1个答案

满玉泽
2023-03-14

编辑2:

我应该问的问题是:

由于Java GSS没有使用凭证缓存,如何防止我的KDC因重复的SGT请求而受到攻击。

我把我的原始答案留在底部,因为它主要集中在原始问题上。

经过另一轮深入调试和测试,我找到了一个可以接受的解决根本问题的方法。

在JAAS中使用JavaGSS API,而不是在我的原始解决方案中使用没有JAAS的“纯”GSS,会有很大的不同!

是的,可能在凭据缓存中的现有服务票证(SGTs)没有被加载,也没有将任何新获取的SGTs写回缓存,但是KDC不会经常被敲击(真正的问题)。

纯GSS和带有JAAS的GSS都使用客户主体。主题有一个内存中的私有凭据集,用于存储TGT和SGT。

关键区别在于:

>

带有JAAS的GSS:主题是由JAAS在GSSContext之外创建的,因此可以在应用程序的生命周期内存在,在应用程序的生命周期内跨越许多GSSContext。

建立的第一个GSSContext将查询主题的私有凭据以获取SGT,而不是找到一个,然后从KDC请求SGT。

SGT被添加到受试者的私人凭证中,由于受试者的寿命长于GSSContext,因此在创建以下GSSContext时,它与SGT一样可用。这些将在受试者的私人证书中找到中士,并且不需要点击KDC来获得新的中士。

因此,从我的特定Java Fat客户端(打开一次并可能运行数小时)来看,一切都正常。创建的第一个GSSContext将命中SGT的KDC,然后由所有后续创建的GSSContext使用,直到客户端关闭。凭据缓存未被使用,但这不会造成影响。

鉴于客户机的寿命要短得多,可能会同时多次重新打开,那么使用/不使用凭据缓存可能是一个更严重的问题。

private void initJAASandGSS() {
    LoginContext loginContext = null;               
    TextCallbackHandler cbHandler = new TextCallbackHandler();
    try {
        loginContext = new LoginContext("wSOXClientGSSJAASLogin", cbHandler);
        loginContext.login();
        mySubject = loginContext.getSubject();
    } catch (LoginException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    gssManager = GSSManager.getInstance();

    try {
        //TODO: LAMB: This name should be got from config / built from config / serviceIdentifier
        serverName = gssManager.createName("HTTP/app-srv.acme.com@ACME.COM", null);
        Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
    } catch (GSSException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();        
    }
}

private String getGSSwJAASServiceToken()  {

    byte[] token = null;
    String encodedToken = null;

    token = Subject.doAs(mySubject, new PrivilegedAction<byte[]>(){
        public byte[] run(){
            try{

                System.setProperty("javax.security.auth.useSubjectCredsOnly","true");
                GSSContext context = gssManager.createContext(serverName,
                    krb5Oid,
                    null,
                    GSSContext.DEFAULT_LIFETIME);

                context.requestMutualAuth(false);
                context.requestConf(false);
                context.requestInteg(true);

                byte[] ret = new byte[0];           
                ret = context.initSecContext(ret, 0, ret.length);

                context.dispose();

                return ret;

            } catch(Exception e){
                Log.log(Log.ERROR, e);
                throw new otms.util.OTMSRuntimeException("Start Client (Kerberos) failed, cause: " + e.getMessage());
            }
        }
    });

    encodedToken = Base64.getEncoder().encodeToString(token);
    return encodedToken;
}

结束编辑2:原始答案如下:

问题1:Java GSS-API是否未将服务票据保存到凭据缓存?或者我可以改变我的代码吗?

编辑:根本原因分析。

经过数小时的sun安全调试。*类,我现在了解了GSS和Java安全代码在做什么/不做什么-至少在Java 8U 131中是这样。

在本例中,我们有一个Java GSS可以访问的凭证缓存,包含一个有效的票据授予票据(TGT)和一个有效的服务票据(SGT)。

1) 创建客户端主体主体时,TGT从缓存(Credentials.acquireTGTFromCache())加载,并存储在主体的PrivateCdentials集中--

仅加载TGT,不加载SGT并将其保存到受试者私人凭据--

2) 后来,深入到GSSContext。initSecContext()过程中,安全代码实际上尝试从主题的私有凭据中检索服务票证。相关代码是Krb5Context。initSecContext()/KrbUtils。getTicket()/SubjectComber.find()/findAux()。然而,由于在步骤1)中从未加载SGT,因此将找不到SGT!因此,从KDC请求并使用一个新的SGT。

对每个服务请求重复此操作。

只是为了好玩,严格来说是作为概念验证黑客,我在登录名和initSecContext()之间添加了几行代码来解析凭据缓存、提取凭据、转换为Krb凭据,并将它们添加到主题的私人凭据中。

完成此操作后,在步骤2)中找到并使用现有的SGT。KDC不请求新的SGT。

我不会发布这个黑客的代码,因为它调用了我们不应该调用的sun内部类,我不希望激励其他人这么做。我也不打算用这个黑客作为解决方案。

a) SGT未从凭据缓存加载到客户端主体的主体

b) 没有公共API或配置设置来执行此操作。

这会影响带JAAS和不带JAAS的GSS-API。

那么我该怎么办?

i) 将Java GSS-API/GSS-API与JAAS“按原样”一起使用,每个SGT请求都会命中KDC-

ii)正如Samson在下面的评论中建议的那样,使用JavaGSS-API仅用于应用程序的初始登录,然后对于所有进一步的调用,使用代币或cookie为后续调用使用替代安全机制(一种自建kerberos-light)。

iii)考虑GSS-API的替代方案,例如Apache Kerby边框客户端。这具有超出本答案范围的含义,并且很可能被证明是从众所周知的煎锅跳到火上。

我已经向Oracle提交了Java功能请求,建议应该从缓存中检索SGTs并存储在主题凭据中(TGTs已经是这种情况)。

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180144

问题2:服务票证没有保存到缓存有什么缺点吗?

使用服务票证的凭据缓存减少了客户端和KDC之间的交互。由此产生的推论是,在不缓存服务票证的情况下,每个请求都需要与KDC进行交互,这可能导致KDC受到攻击。

 类似资料:
  • 我已经编写了一个在Windows下运行的简单JavaHTTP客户端。客户端与需要通过SPNego进行Kerberos身份验证的Web服务器进行通信。 我遇到了两个问题: > 服务票证没有存储在我的凭据缓存中。执行请求后,我希望看到一个Kerberos服务票存储在我的凭据缓存下的

  • 本文向大家介绍java使用hashMap缓存保存数据的方法,包括了java使用hashMap缓存保存数据的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了java使用hashMap缓存保存数据的方法。分享给大家供大家参考,具体如下: 更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇

  • 我试图在使用JGit拉取回购时使用Kerberos身份验证,但出现以下错误: 来自Ticket Cache的空凭据 [Krb5LoginMoules]身份验证失败 这是我的登录名。形态 我使用自定义的JcshConfigSessionFactory和重写配置方法,如下所示 “klist”说凭证缓存是API(我在MacOS Sierra上)。我怎样才能克服这次失败?

  • Quarkus有类似于开箱即用?(在文件中找不到任何提及) 要穿上某种服务方式, 这将在一段时间内保留/缓存prev响应,并保留调用堆栈/日志。 类似于那个Spring注释?

  • 我将React应用程序转换为PWA,它的工作部分正常。 我遵循了本教程:https://medium.com/@toricpope/transform-a-react-app-to-a-progressive-web-app-pwa-dea336bd96e6 然而,本文只展示了如何缓存静态数据,我还需要存储来自服务器的数据,我可以按照本文第一个答案的说明来做:如何将API中的数据缓存到React

  • 从我的Service Worker中,我在浏览器缓存中保存了一些资产,以及Service Worker脚本本身,它工作正常,这样我就可以在我的devols缓存选项卡中看到Service Worker url和所有其他资产。 现在,当我脱机时,我的服务人员将侦听事件并从缓存中获取所有资产。 但是,当页面尝试注册工作程序本身时,似乎没有获取事件,因此我在控制台中遇到以下错误: 控制台错误 控制台ero