Port 389 may not be as much of a problem as they are telling you it is

This article is loosely based on my German blog article from 2024 (here).

Almost every AD security assessment, penetration test or architecture conversation ends up containing the recommendation to “switch from unsecured LDAP to LDAPS” for your Active Directory. Working for a software vendor whose products “do stuff with AD” I hear the question multiple times a week: “Does your product XY support LDAPS and if not, is it on the roadmap?”. In most of the cases, the correct answer to this is “no, and it’s not a security concern“. Let’s look at the WHY.

The Internet offers literally thousands of articles, blogs and videos on how to create and import TLS certificates into your Domain Controllers to make them serve LDAP over TLS on port 636/tcp. If you are interested in the method Domain Controllers will use to select their LDAPS certificate if there are multipla candidates, @awakecoding investigated that for you. What these articles usually will not tell you is what you are supposed to do with it once it’s enabled. One thing is for certain, however: Your DCs will continue serving TLS-less LDAP on port 389, and you cannot disable that or block in on the firewall without severely disrupting your AD members.

Let me repeat: an AD member will always use port 389/3268 for any LDAP/GC queries it has to perform by virtue of its being an AD member. There is no way to change that or to entice a member machine to use the TLS-backed LDAPS on port 636/3269, period. There is also no standardized, universally reliable way to have the infrasturcture advertise LDAPS to applications. Since the only known mechanism for LDAP service discovery is DNS, people have tried multiple things in the past:

  1. add a SRV record for LDAPS, like _ldaps._tcp.domain.com along with _ldap and _gc
  2. change the port number in the LDAP service records from 389 to 636 (and prevent them from being updated by DCs)
  3. replace _ldap SRV records by _ldaps (i.e. actually delete the _ldap and _gc records)

#1 doesn’t change a thing since _ldaps is not a standard service so no application will look for it in DNS. If you create an application that would benefit from LDAP over TLS, you are welcome to implement LDAPS service discovery in your code, then it will work like every custom service discovery. But it has never been an industry standard, and still isn’t so no other application will make use of that.

#2 will not disrupt AD members’ communications because port 389 is hardcoded in Windows. It may or may not disrupt other LDAP-consuming applications, because switching the port from 389 to 636 will not cause them to try and establish a TLS handshake before attempting an LDAP bind.

#3 will break all your service discovery for LDAP, including that of domain members. Don’t do this at home!

So is LDAP in Active Directory “insecure by design”? Quite on the contrary. The reason you’ve been advised to switch to LDAPS is to protect clear-text credentials used by LDAP if you perform a simple bind using them. The thing is, AD members – and most of the applications accessing AD via LDAP – do NOT use simple binds. Instead, they use SASL binds where authentication is performed by GSS-SPNEGO and the LDAP encryption layer is negotiated with session keys exchanged during the authentication process. So not only are there no clear-text credentials to protect, such communication as takes place IS, in fact, encrypted already without having to rely on certificates for the TLS layer. This mechanism is not limited to domain members – any application can do this, regardless of operating system and its domain membership!

So what SHOULD you do to harden AD LDAP if not block port 389?

If you are certain that no application in your environment actually needs simple binds with clear-text credentals, enforce LDAP signing on your DCs by setting the Group Policy as described here. In addition to – well, having the communications signed which will prevent a variety of Kerberos and NTLM relay attacks – the DCs will also reject a simple bind over an unencrypted LDAP connection! If you want to be absolutely sure you’re not disrupting anything, observe your Domain Controllers’ event logs as descrbed in the evergreen Learn article about LDAP signing, Channel Binding and LDAPS.

If you do have applications requiring a simple bind, force these applications to use port 636/3269 by configuring this (and the trust in the LDAPS certificates!!!) within the applications themselves.