we are using JAAS to enable Single Sign On in a Java application using the Windows Kerberos ticket cache. Our jaas.conf config file looks like this:
LoginJaas {
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=true
doNotPrompt=true
debug=true;
};
With this, we can create a Jaas LoginContext and successfully get the user's Kerberos ticket. We send this ticket to out server application using JMI. What we don't manage to do though is to verify on the server that the Kerberos ticket was in fact created by our Active Directory.
At the moment, we do a very insecure validation of the ticket by simply checking if the Server Principal (KerberosTicket.getServer()) name has our domain name in the realm part. But of course, anyone could set up an own Kerberos server with the same realm name and use that ticket to start the application.
One idea I have found was to authenticate against the Active Directory LDAP using the Kerberos ticket. Unfortunately, we use Windows 7 and re-using the Kerberos ticket to authenticate against the LDAP only works when setting a Registry entry (see http://java.sun.com/j2se/1.5.0/docs/guide/security/jgss/tutorials/Troubleshooting.html, search for allowtgtsessionkey). This is unacceptable for our users.
Is there any way to validate the ticket against our Active Directory server? I suspect there is a way to check if the KerberosTicket.getServer() ticket equals the ticket of our server, but I have no idea how to do that. UPDATE: KerberosTicket().getServer() only returns a KerberosPrincipal that contains nothing but the server ticket name and realm, so it is not suitable for validation.
Thanks for your help,
memminger
As you mentioned, the proper way to solve this is by kerberizing your service, which is the whole point of the Kerberos protocol (authenticating clients against servers). Ticket reuse doesn't work exactly because it'd be a security problem if it did.
A Kerberos service does not need to "log into Active Directory", it just needs to have a shared key with AD.
BTW, to get SSO using JAAS requires having that allowtgtsessionkey set, there's no way around that on Windows.
As no one seems to really know an answer to this, I suppose we have to make a proper Kerberos service out of our server application. One that logs in to the Active Directory itself and that has the ServicePrincipalName attribute set properly. Kind of like SPNEGO does for HTTP. A good starting point how to do that will be the SPNEGO Servlet filter on SourceForge (http://spnego.sourceforge.net/). http://thejavamonkey.blogspot.com/2008/04/clientserver-hello-world-in-kerberos.html is also a very good example of how to do Service logon.
Unfortunately, this leads to the same problem with the registry key, so I posted a new question on Is there a way in Java or a command-line util to obtain a Kerberos ticket for a service using the native SSPI API?.
Related
I want to automatically authenticate, 'active directory users', which are logged in to their windows, in my applications.
in short, i want SSO for my applications using windows credentials.
**Client is React and back-end is Java 8 and Spring 4.1.2 and Spring Security is 3.2.5.
I already authenticate and search 'active directory users' in my applications, using spring LDAP 3.2.5.
but users should submit their username and password when they use browser.
I have read about 'Integrated Windows Authentication' (IWA), 'Kerberos', 'NTLM'.
should I use NTLM instead of LDAP ???
or, should I use Kerberos ???
or, should I use ADFS ???
should I config anything in active directory for that ???
**I cant config anything in active directory
should I get windows credentials programmatically in react and send it to server and from server I should send that credentials to active directory to verify it ???
I don't know but, should I say any thing in my 'HTTP response' to 'HTTP OPTION Request' to force browser to set windows credentials in next request ??
and, thanks for your time.
There are a couple ways to do this:
Windows Authentication
This is best for the user as it is a seamless login. If the website is trusted, then the browser will automatically send the credentials of the currently-logged-on user to the site.
In this case, the web server (Tomcat in this case) handles the authentication and passes the credentials to the application. If you were using IIS and Windows, the setup would be super easy. But with Tomcat on Linux, it's a little harder. You need to setup kerberos, which requires setting up SPN (Service Principal Name) values on the domain so that your server is trusted on your domain to authenticate. The full instructions for setting this up in Tomcat 8 are here: Windows Authentication How-To
Once that is setup, your website needs to be trusted by the browsers. If your site is recognized as an intranet site, then this should already be true. If not, then your site's domain needs to be added to the Trusted Sites in the Internet Options on the client computers. This can also be done via Group Policy. That will work for IE and Chrome. Firefox uses its own network.negotiate-auth.delegation-uris setting.
Forms Authentication
Another way is to use a login page to ask the user for their username and password, then authenticate them via LDAP in your Java application code. I will assume you know how to setup a login page, so you just need to know how to verify the credentials. You can use the answer here for that code. That answer has the code in a console app, but you can pull out the code that takes the username and password and verifies it.
This is arguably easier to setup, but at the cost to the user.
We're planning to implement SSO using http://spnego.sourceforge.net/ on our web application. I've tried the sample projects in http://spnego.sourceforge.net/ and it worked well (helloKDC.java and hello_spnego.jsp).
My question is, after authenticating the user successfully using SPNEGO, how can this user be login automatically to our web application? I know, that request.getRemoteUser() returns the client's windows username, but how about the client's windows password? If my understanding is correct, SPNEGO uses tokens instead of passwords?
Note: This question is somewhat similar to Java SSO using SPNEGO but unfortunately, no accepted answer on that.
Your understanding is correct, SPNEGO uses tokens instead of passwords. As to your first question, regarding how can user login automatically to web application, you have to create a Kerberos keytab and then copy it to your application server (and configure it to look at the keytab) in order to decrypt the SPNEGO token to "tell" who the user is. Once a keytab is in place, logins to your web server using Kerberos SSO will then occur automatically. Otherwise the token will just look like scrambled bits to your application and SSO authentication will fail. Note you will never be able to see the password - there is no request.getPassword(). In SPNEGO/Kerberos, the password is never revealed or exposed to anyone, not to the application server and not even to the Kerberos KDC itself, so it is impossible to grab the password in any way when using SPNEGO/Kerberos. That's one of the beauties of Kerberos. For additional reference, you can read more from my technical article on how to create Kerberos keytabs here: Kerberos Keytabs – Explained.
I have configured Kerberos for various authentication purposes. OS command line tools are working and return renewable tickets with a configurable lifetime.
I now need to obtain tickets in a custom application and store them away in a ticket cache file that I can use with any Kerberos system tool. Since the custom application is Java-based, it seems most natural to use Java means to authenticate and deal with the tickets.
I have been following the example given in JAAS Authentication Tutorial and successfully authenticated to my Kerberos KDC. Now, I would like to
Fine tune the properties of the obtained TGT ticket. What I get is a non-renewable ticket with unlimited lifetime. Instead, I would like to request a renewable ticket with configurable lifetime, renewable lifetime etc.
Store the obtained ticket into a ticket cache file for use outside the sample application (e.g. command line kerberos utilities).
I do have two open questions:
How can I configure the login context to obtain tickets with the desired settings?
How can I extract the credentials from the authenticated subject and save them into a ticket cache file?
Update: Digging into the code, it seems that sun.security.krb5.KrbAsReqBuilder is used to do the Kerberos communication. The lifetime options seem to be hardcoded in there, although sun.security.krb5.KrbAsReq allows to specify them. Seems I might need to take another route to get what I want.
Update2: After reviewing other options, I have decided to use a modified version of the original C source code for MIT Kerberos' kinit tool instead of using Java at all.
I want the user to be able to Single-Sign-On, i.e. once logged on as Windows User, all services offered by my application should be accessible without further authentication.
In order to authenticate the user I'm using JAAS (Java Authentication and Authorization Services), which is integrated in Java.
The Java API ships also with a several JAAS LoginModules. One of them is called NTLoginModule, which retrieves user information about the currently logged on Windows User.
Where does NTLoginModule retrieve
its information from?
Can I use the
information returned by NTLoginModule in order to authenticate - in a safe manner -
the current user?
Are there any security issues I have to know about?
Thank you in advance!
I cannot help with JAAS, last tyime I did SSO with NTML, it was based on jCIFS.
However, I'm replying on your third point: There are security issues with NTLM
NTLM is quite weak (even v2), and you should lock accounts after a given number of login failures (to avoid brute force attack).
NTLM cannot work other a firewall.
NTLM token cannot be trusted by a third party, leading to the double-hop problem. Your application cannot take the identity of the logged in user to call another NTLM-protected server (like a web service; a RSS feed; or any web resource).
NTLM is not supported by all browsers. Internet Explorer and Chrome works natively ; Firefox needs to edit a configuration for each targeted site ; Opera, Konqueror don't support NTLM at all.
As I said in the comment to rds' answer: "I learned that NTLoginModule is quite insecure. I was able to fake the returned username by replacing the NTUserPrincipal.class file in the rt.jar package and so I was able to return a bogus user name. So it's not suitable for any kind of authentication."
Additionally, by doing some researches I found out that NTLoginModule retrieves it's information from nt.dll, which is part of the Java native libraries for Windows. nt.dll uses advapi32.dll in order to retrieve the current's user information.
I want to implement Single Sign On with Kerberos in Java and have successfully managed to create a ticket for the Service using the ticket from the Windows logon. Unfortunately, I can only create that ticket when the Registry Key "allowtgtsessionkey" is enabled. I am receiving an exception with the message "Identifier doesn't match expected value (906)" as soon as I disable it. The registry key is documented on http://java.sun.com/j2se/1.5.0/docs/guide/security/jgss/tutorials/Troubleshooting.html and http://support.microsoft.com/kb/308339.
Unfortunately I do not have access to the registry on the computers where my application will be used, so I am looking for a way to do this without having to modify it. When I do Single Sign On over SPNEGO in Internet Explorer or Mozilla Firefox, they create a Service ticket in my ticket cache, so there definitely has to be a way to do this without setting the registry key. Does anyone have an idea how to do this in Java?
Thanks for your help,
memminger
Update: I am giving up on this issue. The Windows registry key prevents the access to the Ticket (more exactly: the Subject) inside the Ticket cache. Java on Windows uses its own GSSAPI implementation, and I suppose that needs access to the Ticket to create a Service Ticket. The SSPI Windows API though has full access to the Ticket cache and can thus create Service tickets. This API is used by the web browsers, but it is not used by Java (according to http://java.sun.com/developer/technicalArticles/J2SE/security/#3). When I disable SSPI in Firefox after having accessed a web page once (so a service ticket has been created), I can still access the page, so maybe a command-line util would be sufficient that creates a service ticket using the SPPI API.
For us, this means now that we can either abandon Single Sign On (which is unacceptable for us) or that we do the authentification on the client side of our application (because we can only read out the username but not verify the ticket on the server), which is a major security risk. Another example of how stronger security constraints lead to bigger security holes because they become too complicated to use.
Forgive me if I am misunderstanding you problem, but...
The point of SSO type systems is that the client authenticates directly to the (separate) authentication server, and obtains a ticket from it. It then passes the ticket to the target server(s) it wants to use, each of which verify that the ticket is valid with the authentication server. If the ticket is validated, it can be assumed by the server that the client only obtained it by presenting the (trusted) Kerberos server with acceptable credentials.
Nowhere in the process, should any server authenticate on behalf of the client. In such a system, the only server that needs to know and validate the client's credentials is the authentication server - no other server need have access to this information. This way the client can authenticate for many servers with just one authentication exchange, and credentials are not put at risk by being stored on, or accessible to, multiple servers.
It sounds like your implementation is working just as it should - the authentication should occur on the client side of the application, and this is correct and not a security risk.
Have you tried setting sun.security.jgss.native in Java 6? Wouldn't SSPI be the "native" interface for windows?
You can access the native SSPI API via JNA. See the WindowsAuthProviderImpl in WAFFLE or WindowsNegotiateScheme from the Apache HC library for an example.
Native support for Windows SSPI was introduced into JDK 13 and later backported to JDK 11 too. You'll need to use at least Java 11.0.10. When the JDK's support for SSPI is used then there's no longer a need to fiddle with the allowtgtsessionkey registry key, nor any need to use JNA or Waffle.
You need to set
-Dsun.security.jgss.native=true
to make it work.
You can recognize if your JDK version for Windows has support for SSPI if it includes a file named sspi_bridge.dll in the bin directory.
Refs:
JDK-6722928