Why LDAPS Instead of Plain LDAP

By default, LDAP communications between clients and domain controllers on Windows Server 2022 travel over port 389 in plaintext. Every credential bind, directory query, and object modification is transmitted without encryption, making it trivial for any attacker with network access to capture password hashes, read sensitive directory attributes, or perform a man-in-the-middle attack against an LDAP session.

LDAP over SSL (LDAPS) encrypts all LDAP traffic using TLS, operating on port 636 (and port 3269 for the Global Catalog over SSL). When a domain controller holds a valid server authentication certificate in its certificate store, it will automatically offer LDAPS without any additional configuration beyond importing the certificate.

Microsoft has progressively tightened LDAP security defaults. As of updates released in 2020, Active Directory domain controllers enforce LDAP Channel Binding and LDAP Signing, making LDAPS a hard requirement for any application that performs credential binds. Applications that use Simple Authentication and Security Layer (SASL) over unsigned LDAP will fail. Migrating to LDAPS protects against credential interception, relay attacks, and unsigned LDAP injection.

Generating and Installing the DC Certificate

A domain controller enables LDAPS automatically when it finds a valid Server Authentication certificate (OID 1.3.6.1.5.5.7.3.1) in the local machine’s Personal certificate store. The certificate must be issued for the DC’s fully qualified domain name (FQDN).

If you have an internal Certificate Authority (AD CS), request a certificate using the built-in Domain Controller Authentication or Kerberos Authentication template. From the DC, open an elevated PowerShell session:

# Enroll for a DC certificate from the enterprise CA
certreq -enroll -machine "Domain Controller Authentication"

Or use the Certificates MMC snap-in (for Local Computer > Personal > Certificates), right-click, All Tasks > Request New Certificate, and select the Domain Controller Authentication template.

To verify the certificate has been issued and is in the right store:

Get-ChildItem Cert:LocalMachineMy | Where-Object {
  $_.EnhancedKeyUsageList -match "Server Authentication"
} | Select-Object Subject, Thumbprint, NotAfter

If you are using an external or third-party CA, import the signed certificate and the full chain into the DC’s certificate store:

# Import the certificate PFX (includes private key)
Import-PfxCertificate -FilePath C:certsdc01.corp.pfx `
  -CertStoreLocation Cert:LocalMachineMy `
  -Password (ConvertTo-SecureString "PfxPassword123!" -AsPlainText -Force)

# Import the CA chain into Trusted Root Certification Authorities
Import-Certificate -FilePath C:certsRootCA.cer `
  -CertStoreLocation Cert:LocalMachineRoot

After a new certificate is placed in the store, the LSASS process picks it up without requiring a DC reboot. You may need to restart the Active Directory Domain Services service or wait up to a minute for the DC to begin advertising LDAPS.

Verifying LDAPS Is Working with ldp.exe

The quickest way to confirm LDAPS is operational is using ldp.exe, included with the RSAT tools on any Windows machine.

ldp.exe

In ldp.exe: choose Connection > Connect, enter the DC FQDN (e.g., dc01.corp.example.com), set port to 636, and check the SSL checkbox. Click OK. A successful connection result pane will show the DC’s RootDSE object entries. If you see a TLS handshake error, the certificate is not trusted by the client machine — ensure the issuing CA chain is in the client’s Trusted Root store.

Verify port 636 is listening on the DC:

netstat -ano | findstr :636

Test from PowerShell on a remote client:

$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.Connect("dc01.corp.example.com", 636)
$tcp.Connected

Forcing LDAP Channel Binding via Group Policy

LDAP Channel Binding (CBT) prevents man-in-the-middle attacks by tying the LDAP authentication token to the specific TLS channel in use. It should be set to Always in production environments. Configure via Group Policy on the Domain Controllers OU.

Navigate to: Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options

Set “Domain controller: LDAP server channel binding token requirements” to Always.

You can also configure this with PowerShell by editing the registry (applicable to all DCs via GPO or direct edit for testing):

Set-ItemProperty -Path "HKLM:SYSTEMCurrentControlSetServicesNTDSParameters" `
  -Name "LdapEnforceChannelBinding" `
  -Value 2 `
  -Type DWord

Values: 0 = Disabled, 1 = Supported (when offered by client), 2 = Required (Always). Setting 2 means clients that cannot provide a channel binding token will be rejected.

Forcing LDAP Signing via Group Policy

LDAP Signing prevents unsigned LDAP binds and protects against tampering with LDAP responses in transit. This is separate from LDAPS (encryption) — LDAP Signing can be enforced over port 389 as well, but LDAPS is the preferred solution since it provides both encryption and signing.

In Group Policy: Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options

Set “Domain controller: LDAP server signing requirements” to Require signing.

Registry path for this setting:

Set-ItemProperty -Path "HKLM:SYSTEMCurrentControlSetServicesNTDSParameters" `
  -Name "LDAPServerIntegrity" `
  -Value 2 `
  -Type DWord

Values: 0 = None, 1 = Negotiate (sign if client requests), 2 = Require (reject unsigned binds). Apply this policy carefully; any application using an anonymous or simple unsigned LDAP bind will fail immediately when this is set to 2. Audit unsigned binds first (see below) before enforcing.

Auditing Unsigned LDAP Binds Before Enforcing

Before enforcing LDAP Signing or Channel Binding, identify applications currently using unsigned or simple binds so you can migrate them to LDAPS first. Enable diagnostic logging on the DC to capture these events:

Set-ItemProperty -Path "HKLM:SYSTEMCurrentControlSetServicesNTDSDiagnostics" `
  -Name "16 LDAP Interface Events" `
  -Value 2 `
  -Type DWord

After enabling, check the Directory Service event log for Event ID 2886 (unsigned bind summary) and 2888 (unsigned bind detail per connection). Event ID 2887 lists the number of unsigned binds per 24 hours.

Get-WinEvent -LogName "Directory Service" | Where-Object {$_.Id -in @(2886,2887,2888)} | Format-List TimeCreated, Id, Message

Turn off diagnostic logging after your audit to avoid excessive log volume:

Set-ItemProperty -Path "HKLM:SYSTEMCurrentControlSetServicesNTDSDiagnostics" `
  -Name "16 LDAP Interface Events" `
  -Value 0 `
  -Type DWord

Testing LDAPS Authentication with PowerShell

Verify that your applications can authenticate via LDAPS by performing a test bind from PowerShell using the System.DirectoryServices.DirectoryEntry class:

$ldapsPath = "LDAP://dc01.corp.example.com:636/DC=corp,DC=example,DC=com"
$username = "corptestuser"
$password = "TestPassword123!"

try {
    $entry = New-Object System.DirectoryServices.DirectoryEntry($ldapsPath, $username, $password)
    $searcher = New-Object System.DirectoryServices.DirectorySearcher($entry)
    $searcher.Filter = "(sAMAccountName=testuser)"
    $result = $searcher.FindOne()
    if ($result) { Write-Host "LDAPS bind and search succeeded: $($result.Path)" }
} catch {
    Write-Host "LDAPS test failed: $($_.Exception.Message)"
}

You can also test using ldifde over port 636:

ldifde -f C:test_ldaps.ldf -s dc01.corp.example.com:636 `
  -t 636 `
  -d "DC=corp,DC=example,DC=com" `
  -p subtree `
  -r "(sAMAccountType=805306368)" `
  -l "cn,mail" `
  -z

Configuring the Firewall for LDAPS

Ensure port 636 (LDAPS) and port 3269 (Global Catalog LDAPS) are permitted through the Windows Firewall on the DC and any network firewalls between clients and DCs:

# Allow LDAPS inbound
New-NetFirewallRule -DisplayName "LDAPS Inbound" `
  -Direction Inbound `
  -Protocol TCP `
  -LocalPort 636 `
  -Action Allow `
  -Profile Domain,Private

# Allow Global Catalog LDAPS inbound
New-NetFirewallRule -DisplayName "GC LDAPS Inbound" `
  -Direction Inbound `
  -Protocol TCP `
  -LocalPort 3269 `
  -Action Allow `
  -Profile Domain,Private

Configuring Applications to Use LDAPS

Most LDAP-capable applications have a setting to switch from LDAP (port 389) to LDAPS (port 636). The general changes required are:

  • Change the LDAP server address to use ldaps://dc01.corp.example.com (note the ldaps:// scheme)
  • Change the port from 389 to 636
  • Ensure the application trusts the CA that issued the DC’s certificate (install the CA root certificate in the application’s trust store or the OS trust store)
  • For Java applications: import the CA cert into the JVM’s cacerts keystore using keytool
  • For Linux/Unix applications querying AD via LDAPS: add the CA cert to /etc/ssl/certs/ and run update-ca-certificates

Troubleshooting LDAPS Certificate Errors

Common LDAPS issues and their resolutions:

Problem: TLS handshake fails immediately.
Cause: The DC certificate is not trusted by the client, or the certificate has expired.
Resolution: Check the DC certificate thumbprint, verify the CA chain is complete, and confirm the client has the root CA certificate installed.

# On the client, verify trust
Test-NetConnection -ComputerName dc01.corp.example.com -Port 636

# Check certificate validity on DC
Get-ChildItem Cert:LocalMachineMy | Where-Object {$_.Subject -like "*dc01*"} | Select-Object Subject, Thumbprint, NotAfter, NotBefore

Problem: Certificate name mismatch.
Cause: The certificate was issued for a different name than the DC’s FQDN.
Resolution: The certificate’s Subject or Subject Alternative Name (SAN) must exactly match the FQDN clients use to reach the DC. Reissue the certificate with the correct SAN:

Get-ChildItem Cert:LocalMachineMy | Select-Object Subject, DnsNameList, Thumbprint

Problem: LDAPS works but channel binding fails.
Cause: The client is sending a channel binding token that does not match the DC’s certificate.
Resolution: Ensure the certificate in use on the DC has not changed since the channel binding was negotiated. Verify with Event ID 3039 (successful channel binding) or 3040 (failed channel binding) in the Directory Service event log.

Problem: DC does not listen on port 636 after certificate install.
Cause: LSASS has not yet picked up the new certificate.
Resolution: Restart the Active Directory Domain Services service (or reboot if practical). Check with netstat -ano | findstr :636 to confirm the port becomes active.

LDAPS is a foundational security control for any Active Directory environment. Combined with LDAP Signing and Channel Binding enforcement, it eliminates the most common attack vectors against directory authentication traffic and is a prerequisite for compliance with PCI-DSS, HIPAA, and similar regulatory frameworks.