“检测到缺陷令牌”错误(NTLM 不是 Kerberos)与 Kerberos/Spring Security/IE/Active Directory

Posted

技术标签:

【中文标题】“检测到缺陷令牌”错误(NTLM 不是 Kerberos)与 Kerberos/Spring Security/IE/Active Directory【英文标题】:"Defective token detected" error (NTLM not Kerberos) with Kerberos/Spring Security/IE/Active Directory 【发布时间】:2011-02-27 17:41:58 【问题描述】:

我们无法让 Spring Security/Kerberos/AD 为我们的 Web 应用程序工作。我们的诊断是我们的 AD 服务器向 IE 发送了一个 NTLM 令牌(我们可以知道它以“TlRMTVNT .....”开头),然后 IE 将它发送到我们的应用程序并且它失败了。我们的 AD 服务器应该向 IE 发送 Kerberos/SPNEGO 令牌。

“活动部件”如下:

Spring Security 3.0(已修补) Microsoft Windows Server Enterprise 2003 SP1 活动目录 IE 8 Tomcat(TC 服务器 6.0) Java 1.6

我们已按照此处的说明进行了详细设置:

https://spring.io/blog/2009/09/28/spring-security-kerberos-spnego-extension

这涉及到:

创建一个普通用户作为服务主体(与我们的应用程序所在的机器名称相同)。我们设置以下帐户选项: disabled 'Use has to change password at next login' 启用“密码永不过期” 启用“使用 Kerberos DES…” disabled '不需要 Kerberos 预身份验证' 注意:Server 2003 不提供“此帐户支持 Kerberos AES 128 位...”和“此帐户支持 Kerberos AES 256 位...”选项 使用“ktpass.exe”将服务主体名称 (SPN) 分配给此新用户并将此用户密钥导出到密钥表文件。使用 'ktpass /out ourweb.keytab /mapuser ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK /princ HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK /pass * ' 从https://src.springframework.org/svn/se-security/trunk下载源代码。 将keytab文件从AD服务器复制到源代码(应用程序)的WEB-INF/etc。 更改文件 SunJaasKerbersoTicketValidator.java 以读取 keytab 文件。 (解决应用程序无法从 Java 类路径读取 keytab 文件的问题) options.put("keyTab", "C:\se-security\spring-security-kerberos\spring-security-kerberos-sample\src\main\webapp\WEB-INF\etc\ourweb.keytab");李> 将 web.xml 配置为使用 spnego.xml。 上下文配置位置 /WEB-INF/spnego.xml 通过提供我们的服务主体名称和 keytab 文件位置,将 Spring Security (spnego.xml) 配置为使用 Kerberos(SpnegoEntryPoint、SpnegoAuthenticationProcessingFilter 和 KerberosServiceAuthenticationProvider bean)。 配置 spnego.xml 以读取在 WEB-INF/etc 中复制的 keytab 文件。

当我们启动我们的 TC 服务器时,我们可以看到一切都很好地初始化(即没有错误 - “从 keytab 获得的原则密钥”):

Creating instance of bean 'org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator#10fa4b8' 
Invoking afterPropertiesSet() on bean with name 'org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator#10fa4b8' 
Config name: C:\WINDOWS\krb5.ini
Debug is  true storeKey true useTicketCache false useKeyTab true doNotPrompt true ticketCache is null isInitiator false KeyTab is C:\se-security\spring-security-kerberos\spring-security-kerberos-sample\src\main\webapp\WEB-INF\etc\ourwebapp4.keytab refreshKrb5Config is false principal is HTTP/ourwebappweb4.testdomain.ourcompany.co.uk tryFirstPass is false useFirstPass is false storePass is false clearPass is false
>>> KeyTabInputStream, readName(): TESTDOMAIN.OURCOMPANY.CO.UK
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName(): ourweb
>>> KeyTab: load() entry length: 78; type: 1
>>> KeyTabInputStream, readName(): TESTDOMAIN.OURCOMPANY.CO.UK
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName(): ourweb.testdomain.ourcompany.co.uk
>>> KeyTab: load() entry length: 113; type: 1
Added key: 1version: 2
Ordering keys wrt default_tkt_enctypes list
default etypes for default_tkt_enctypes: 1.
0: EncryptionKey: keyType=1 kvno=2 keyValue (hex dump)=
0000: 91 01 43 E3 02 A8 B9 83   

principal's key obtained from the keytab
principal is HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK
EncryptionKey: keyType=1 keyBytes (hex dump)=0000: 91 01 43 E3 02 A8 B9 83   
Added server's keyKerberos Principal HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UKKey Version 2key EncryptionKey: keyType=1 keyBytes (hex dump)=
0000: 91 01 43 E3 02 A8 B9 83   

[Krb5LoginModule] added Krb5Principal  HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK to Subject Commit Succeeded 

Finished creating instance of bean 'org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator#10fa4b8' 

准备好进行测试,然后我们在 IE 中启用“Windows 集成身份验证”,并确保该域在 IE 的本地 Intranet 站点部分中列出。然后,我们使用完全限定域名连接到我们的 Web 应用程序。

当我们这样做时,我们在浏览器中收到以下错误:

500 Internal server error.

在 TC Server 日志文件中:

Negotiate Header was invalid: Negotiate     TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAFASgKAAAADw== 
  org.springframework.security.authentication.BadCredentialsException: Kerberos validation not succesfull
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:74)
  at org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:92)
  at org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:120)
  at org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:48)
  at org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:132)
  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
  at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
  at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
  at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
  at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
  at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
  at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
  at com.springsource.metrics.collection.web.HttpRequestMetricCollectionValve.invoke(HttpRequestMetricCollectionValve.java:44)
  at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
  at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
  at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
  at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
  at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
  at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
  at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:379)
  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
  at java.lang.Thread.run(Thread.java:619)
Caused by: java.security.PrivilegedActionException: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)
  at java.security.AccessController.doPrivileged(Native Method)
  at javax.security.auth.Subject.doAs(Subject.java:396)
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:72)
  ... 25 more
Caused by: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)
  at sun.security.jgss.GSSHeader.<init>(GSSHeader.java:80)
  at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:287)
  at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:267)
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:161)
  at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:1)
  ... 28 more
SecurityContextHolder now cleared, as request processing completed

似乎(据我们所知)AD 服务器向 IE 发送 NTLM 令牌(我们可以知道它以“TlRMTVNT .....”开头),然后 IE 将其发送到我们的应用程序,然后它失败了。

我们的 AD 服务器应该向 IE 发送一个 Kerberos/SPNEGO 令牌。

其他说明:

我们的服务器(tc 服务器)和客户端(浏览器)在不同的(虚拟)机器上并且在同一个域中。

【问题讨论】:

只想说这是我在 SO,超级工作中看到的最详细的问题之一! 【参考方案1】:

当您在同一台机器上运行客户端和服务器时,可能会发生这种情况。当您使用 IE 与运行 tomcat 的机器对话时,请确保它们是不同的机器。

此外,您需要确保服务器计算机已加入密钥表 (testdomain.ourcompany.co.uk) 中指定的域,否则您可能会退回到 NTLM。即使您的服务器位于未加入域的计算机上,您的 keytab 仍然可以工作(您会看到您展示的漂亮的 keytab 解密),但 IE 可能会感到困惑并且不会做正确的事情。

AD 只真正喜欢为 Server 2003 使用 arcfour-hmac,因此您需要确保在 krb5.ini 文件中正确设置。

您可以像这样正确创建密钥表:

C:\>ktpass -princ HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK -mapuser ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK -crypto RC4-HMAC-NT -ptype K
RB5_NT_PRINCIPAL -pass * -out ourweb.keytab
Targeting domain controller: test-dc.ourcompany.co.uk
Using legacy password setting method
Successfully mapped HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK to ourweb.testdomain.ourcompany.co.uk.
Key created.
Output keytab to ourweb.keytab:
Keytab version: 0x502
keysize 75 HTTP/ourweb.testdomain.ourcompany.co.uk@TESTDOMAIN.OURCOMPANY.CO.UK ptype 1 (KRB5_NT_PRINCIPAL)
vno 3 etype 0x17 (RC4-HMAC) keylength 16 (0x0fd0e500225c4fca9a63a9998b17ca32)

我没有看到您设置了 krb5.ini 文件。您需要在服务器机器上正确设置(默认位置 C:\WINDOWS\krb5.ini):

[domain_realm]  
    .testdomain.ourcompany.co.uk = TESTDOMAIN.OURCOMPANY.CO.UK
    testdomain.ourcompany.co.uk = TESTDOMAIN.OURCOMPANY.CO.UK

[libdefaults]   
    default_realm = TESTDOMAIN.OURCOMPANY.CO.UK
    permitted_enctypes = aes128-cts aes256-cts arcfour-hmac-md5 
    default_tgs_enctypes = aes128-cts aes256-cts arcfour-hmac-md5 
    default_tkt_enctypes = aes128-cts aes256-cts arcfour-hmac-md5 

[realms]    
VERDAD.LOCAL =         
    kdc = test-dc.ourcompany.co.uk  
    admin_server = test-dc.ourcompany.co.uk
    default_domain = TESTDOMAIN.OURCOMPANY.CO.UK

您可能还需要设置以下属性(如果您尝试从 IDE 运行它):

<systemProperties>
  <java.security.krb5.kdc>test-dc.ourcompany.co.uk</java.security.krb5.kdc>
  <java.security.krb5.realm>TESTDOMAIN.OURCOMPANY.CO.UK</java.security.krb5.realm>
</systemProperties>

我在 maven 中使用了 org.codehaus.mojo 插件,它将这些设置在 pom 文件中,如下所示:

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>tomcat-maven-plugin</artifactId>
      <configuration>
        <server>tomcat-development-server</server>
        <port>8080</port>
        <path>/SecurityTest</path>
        <systemProperties>
          <java.security.krb5.kdc>test-dc.ourcompany.co.uk</java.security.krb5.kdc
          <java.security.krb5.realm>TESTDOMAIN.OURCOMPANY.CO.UK</java.security.krb5.realm>
        </systemProperties>
      </configuration>
    </plugin>
  </plugins>
</build>

【讨论】:

感谢@Grant。很好的答案。我们检查并在不同的机器上运行客户端和服务器,并且服务器在域上。但是,您指出我们的 keytab.ini 文件存在问题。再次感谢。我们还将尝试 Maven 技巧。这是“超越”的,非常感谢。 谢谢@MukulGoel 我考虑过整理一篇关于此的帖子/wiki,但认为 *** 是记录此内容的好地方。 您可能在这 6 年中忘记了,但这是否意味着无法在本地测试您已在 Spring 应用程序中正确配置了 Kerberos 安全性? @lilalinux 你不能在一台机器上做 Kerberos。你需要一个 KDC(现在通常是 Microsoft Active Directory)、一个客户端(需要一个密钥)和一个服务器(你需要一个密钥for).这些不能都在同一台机器上,即使是为了测试目的。您需要设置所有这些。这可能会帮助您更好地理解它:cmf.nrl.navy.mil/krb/kerberos-faq.html#servers 我们确实有一个广告,它对除一个用户外的所有人都有效。我们发现,他没有在他的 Windows 机器上登录域。【参考方案2】:

我也遇到过这个问题。 对于那些以后会遇到这个问题的不幸的人来说,这个问题的另一个原因是通过ip而不是A记录(主机名)访问服务器

【讨论】:

【参考方案3】:

我也有同样的问题,我花了很长时间才找到罪魁祸首。因此,如果您已完成上述所有操作,但它仍然使用 NTLM 令牌而不是 kerberos。确保您没有重复的 SPN。在我的情况下,我有 2 个帐户映射到同一个 SPN,原因是我之前在同一台服务器上运行了一个单独的 Web 应用程序,该服务器使用不同的服务帐户但映射到同一个 SPN,即 HTTP/

希望对你有帮助

【讨论】:

【参考方案4】:

所以就我而言,我设置了两台机器:

Win-Root - 192.168.1.100 - 我的测试域控制器 Win-Child - 192.168.1.200 - 将运行由 Kerberos 保护的 Tomcat 服务的服务器

然后我在 Win-Root 上安装了 Active Directory 域服务,并提升为域控制器(启用 DNS)。域名为Netbios=FUSIONIS, FQDN=fusionis.life

然后我进入Win-Child,将DNS服务器设置为192.168.1.100,然后加入域FUSIONIS

然后我进入 Win-Root DNS 设置并为fusion.fusoinis.life 添加了一个正向查找区域。

然后我在http://fusion.fusionis.life:8080 上为我的tomcat 应用启用了Kerberos 身份验证。

我登录 Win-Child 进行测试。

但我不断收到(Mechanism level: GSSHeader did not find the right tag)

头疼了一个小时后,我意识到...... 我不小心以本地帐户登录! 换句话说,whoami 返回“WIN-23523h253\Administrator”。我完全忘记了以域帐户实际登录 Win-Child 服务器。

然后我登录fusionis\testuser,一切正常。

经验教训:确保您没有意外以非域帐户登录!

【讨论】:

以上是关于“检测到缺陷令牌”错误(NTLM 不是 Kerberos)与 Kerberos/Spring Security/IE/Active Directory的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法验证是不是使用了 Kerberos?

德尔福。 WinInet + 思科 + NTLM。 12045 和 12057 错误

git clone 是不是通过 NTLM 代理工作?

在 url 中使用别名时 NTLM 而不是 Kerberos 和 Chrome 版本 69

NET命令用不了,提示:不是内部或外部命令

curl:如何在 Windows 上使用 Kerberos 而不是 NTLM 身份验证?