Some time ago we started to create our own intranet called CoffeeNet, which is a microservice landscape based on our own Spring Boot Starters, a service discovery and an OAuth2 service. The vision is to create a system with a very easy integration of new applications by just adding a few Spring Boot Starters and starting to code the specific functionality of the new service.
We passed that stage of the developer friendly integration, started to look at the security and vulnerability of the system where we tried to make things more secure. At some point we talked about SSL encryption of the traffic between the OAuth2 service and our LDAP. We used Spring LDAP for the communication between these two services and started reading the documentation on how to get this done. We achieved this quite easily by replacing the authentication strategy in the LDAP context source with the provided tls authentication strategy.
Spring is doing a good job of an easy configuration, thanks guys.
We activated the TLS authentication strategy and it was not safe anymore
At the beginning we were happy but then we started our tests and there it happened. Our tests failed because it was very easy to get access via our OAuth2 service with an activated TLS authentication strategy. You just have to know a username that is defined in our LDAP and it will grant access with every password you want to use. That is not even security through obscurity. Just a friendly bouncer.
Not so good for us so we started to dig into the code, maybe it was a mistake on our side. After a long debugging session with two instances of our OAuth2 service, with and without the TLS authentication strategy, we found out that the last bind to the LDAP server, which should contain the user credentials, was an anonymous bind.
This anonymous bind returned with the message "Yeah OAuth2 server, you can let the anonymous user log in, he is secure". Of course that last anonymous bind was not ok. Was it our fault or does Spring LDAP have a bug with anonymous binds and the TLS authentication strategy?
We started to isolate the bug and created a docker container with SSL encryption, activated anonymous binds and allowed anonymous user search through the directory tree.
After some time we had our docker container together with all the configuration we needed to reproduce the bug, hopefully. A small Spring Boot project with Spring LDAP was easy to set up and we configured everything. We logged in with correct credentials and everything was fine.
Then after all this debugging and a good session with my colleagues from synyx, thanks to all of you - that was a really nice Friday with you - we tried to login with the best incorrect credentials "user" and the password "wurstsalat". Maybe you know what happened, we were logged in and had a good feeling from chasing, finding and isolating this bug.
Was it only the authentication strategy that caused that bug?
It was not only the authentication strategy. It was a combination of three things:
- 'Groupsearch' is activated and used
- Anonymous binds are activated and allowed to search through the directory tree
- 'DefaultTlsDirContextAuthenticationStrategy' is used as authentication strategy
When you know what is happening, you can easily see the problems with the
At the beginning of the communication a TLS connection is started and a lookup of the user
cn=user is made:
... 588241ed conn=1011 fd=16 TLS established tls_ssf=128 ssf=128 588241ed conn=1011 op=1 BIND dn="" method=128 588241ed conn=1011 op=1 RESULT tag=97 err=0 text= 588241ed conn=1011 op=2 SRCH base="ou=People,dc=example,dc=org" scope=2 deref=3 filter="(cn=user)" 588241ed < = bdb_equality_candidates: (cn) not indexed 588241ed conn=1011 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text= ...
then the second bind is made, which should contain the credentials but
... 588241ee conn=1013 fd=16 TLS established tls_ssf=128 ssf=128 588241ee conn=1013 op=1 BIND dn="" method=128 588241ee conn=1013 op=1 RESULT tag=97 err=0 text= 588241ee conn=1013 op=2 SRCH base="ou=Groups,dc=example,dc=org" scope=1 deref=3 filter="(member=cn=user,ou=people,dc=example,dc=org)" ...
you can see the empty
dn="" which means the user will be logged in without any authentication. If you want to take a deeper look you can check out the sample project
Let's contribute to Spring LDAP
We opened a pull request with a recommendation how to fix this issue and as you can see below the last bind will now contain the user credentials
... 58824373 conn=1015 fd=16 TLS established tls_ssf=128 ssf=128 58824373 conn=1015 op=1 BIND dn="cn=user,ou=People,dc=example,dc=org" method=128 58824373 conn=1015 op=1 RESULT tag=97 err=49 text= ...
and users with wrong credentials will not be allowed to log in.
A long but satisfying journey
It took as quite some time to get near the bug and understand it, but in the end we learned something again
- Have valuable integration tests
- Do not give up and debug as deep as you can. You can learn a lot.
- Open source projects are there to contribute
- It is fun to chase some bugs 🙂
I hope you enjoyed the blog and it will make you curious what the CoffeeNet will be.