Repository navigation
fix(security): T2.19 hardening: LdapEncoder.filterEncode + entry-point NUL/control-char rejection in PSLdapMembershipAuthProvider (issue #133) - #134
Merged
Conversation
…t NUL/control-char rejection in PSLdapMembershipAuthProvider (issue #133) Spring LDAP 2.4.4 is the last Java 1.8 line; 3.x requires Java 11+. The CVE-2023-46527 class (LDAP injection via JNDI search filter substitution) cannot be closed by a version bump, so the fix has to be at the call site. PSLdapMembershipAuthProvider.searchForUser passes the bind principal as the {0} argument to SpringSecurityLdapTemplate.searchForSingleEntryInternal in a JNDI Context.search(...). JNDI's substitution only expands the {0} placeholder; it does NOT escape LDAP filter metacharacters '(', ')', '*', '\', or NUL. A principal containing any of these would be interpreted as filter syntax, allowing an authenticated low-privileged user to broaden the user search to other directory entries. Two hardening changes: 1. searchForUser (line ~364): wrap the bindPrincipal in LdapEncoder.filterEncode(bindPrincipal) before passing it as the {0} argument. LdapEncoder is the public final utility in org.springframework.ldap.support (spring-ldap-core, already on the classpath). It is a no-op for principals without metacharacters, so valid usernames flow through unchanged. 2. doAuthentication (line ~149): reject null usernames and any username containing a NUL byte or C0/C1 control character. LDAP usernames are restricted to printable characters by RFC 4519. The failure is surfaced as a BadCredentialsException, so the audit trail is consistent with existing failed-login handling. PSLdapUserDetailsMapper is untouched: the XML loading path already routes through PSSecureXMLUtils.getSecuredDocumentBuilderFactory, and there is no LDAP filter substitution in that file. The class is annotated @deprecated (it is part of the deprecated secure-membership module) but is still on the classpath and is still scanned by Dependabot, so the CVE class has to be closed at the call site until the module is removed. Full reactor clean install green on Java 1.8 in 3:46. > Co-Authored by Mavis Mavis-Code using MiniMax-M3 with agent mavis.
4 tasks done
natechadwick
approved these changes
Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
T2.19 of the parent epic #73 — harden Spring LDAP 2.4.4 against LDAP injection (CVE-2023-46527 class) at the only two production call sites in the project.
Closes #133.
The fix is the canonical Spring Security recommendation: encode the principal before it is substituted into the configured
userSearchFilterviaLdapEncoder.filterEncode(...), and reject obviously-malformed principals (NUL bytes, C0/C1 control characters) at the entry point so the failure is fast and the audit trail is clean.Changes
deliverytiersuite/.../secure-membership/.../PSLdapMembershipAuthProvider.java— 1 file, 45 insertions, 1 deletion:doAuthentication(entry point) — reject anyUsernamePasswordAuthenticationToken.getName()that is null or contains a NUL byte / C0 / C1 control character. LDAP usernames are restricted to printable characters by RFC 4519. The failure is surfaced asBadCredentialsException, consistent with existing failed-login handling.searchForUser(filter substitution site) — wrap thebindPrincipalinLdapEncoder.filterEncode(...)before passing it as the{0}argument toSpringSecurityLdapTemplate.searchForSingleEntryInternal. JNDI'sContext.search(...)only expands the{0}placeholder — it does not escape LDAP filter metacharacters(,),*,\, NUL.LdapEncoderis the public final utility inorg.springframework.ldap.support(inspring-ldap-core:2.4.4, already on the classpath) and is a no-op for principals without metacharacters.PSLdapUserDetailsMapperis untouched: the XML loading path already routes throughPSSecureXMLUtils.getSecuredDocumentBuilderFactory, and there is no LDAP filter substitution in that file.Why at the call site
org.springframework.ldap:spring-ldap-core:2.4.4is the last Java 1.8-compatible line. The 3.x line requires Java 11+ and is on a future-migration track. The CVE-2023-46527 class cannot be closed by a version bump, so the fix has to be at the call site.Surface
The only two production files in the entire project that use Spring LDAP are these two (plus the
secure-membershipmodule'spom.xml). The class is@Deprecated(thesecure-membershipmodule is on a deprecation track) but is still on the classpath and is still scanned by Dependabot, so the CVE class has to be closed at the call site until the module is removed.Verification
./mvn-env.sh clean install -DskipTests→ BUILD SUCCESS, 3:46, 60/60 modules.What this PR does NOT do
secure-membershipmodule (separate effort).bindAsUser— JNDI'sInitialLdapContextauthentication rejects malformed DNs at the server, so the bind DN is safe by construction. The injection vector is specifically the search filter substitution.