{"record":{"id":"4e72b8a9b3659879","repo":"spring-projects/spring-security","slug":"bad-credentials-4e72b8","errorCode":null,"errorMessage":"Bad credentials","messagePattern":"Bad credentials","errorType":"exception","errorClass":"BadCredentialsException","httpStatus":null,"severity":"error","filePath":"ldap/src/main/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticator.java","lineNumber":125,"sourceCode":"\t\tif (user == null) {\n\t\t\tthrow UsernameNotFoundException.fromUsername(username);\n\t\t}\n\t\tif (logger.isTraceEnabled()) {\n\t\t\tlogger.trace(LogMessage.format(\"Comparing password attribute '%s' for user '%s'\",\n\t\t\t\t\tthis.passwordAttributeName, user.getDn()));\n\t\t}\n\t\tif (this.usePasswordAttrCompare && isPasswordAttrCompare(user, password)) {\n\t\t\tlogger.debug(LogMessage.format(\"Locally matched password attribute '%s' for user '%s'\",\n\t\t\t\t\tthis.passwordAttributeName, user.getDn()));\n\t\t\treturn user;\n\t\t}\n\t\tAssert.notNull(password, \"LDAP password cannot be null\");\n\t\tif (isLdapPasswordCompare(user, ldapTemplate, password)) {\n\t\t\tlogger.debug(LogMessage.format(\"LDAP-matched password attribute '%s' for user '%s'\",\n\t\t\t\t\tthis.passwordAttributeName, user.getDn()));\n\t\t\treturn user;\n\t\t}\n\t\tthrow new BadCredentialsException(\n\t\t\t\tthis.messages.getMessage(\"PasswordComparisonAuthenticator.badCredentials\", \"Bad credentials\"));\n\t}\n\n\tprivate boolean isPasswordAttrCompare(DirContextOperations user, @Nullable String password) {\n\t\tString passwordAttrValue = getPassword(user);\n\t\treturn this.passwordEncoder.matches(password, passwordAttrValue);\n\t}\n\n\tprivate @Nullable String getPassword(DirContextOperations user) {\n\t\tObject passwordAttrValue = user.getObjectAttribute(this.passwordAttributeName);\n\t\tif (passwordAttrValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (passwordAttrValue instanceof byte[]) {\n\t\t\treturn new String((byte[]) passwordAttrValue);\n\t\t}\n\t\treturn String.valueOf(passwordAttrValue);\n\t}","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/ldap/src/main/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticator.java#L107-L143","documentation":"Spring Security LDAP's PasswordComparisonAuthenticator throws BadCredentialsException(\"Bad credentials\") in authenticate() when the user was found via DN/search but the password comparison failed. Either the password attribute value did not match via the configured PasswordEncoder, or the LDAP bind-based comparison was rejected. It deliberately uses the generic message to avoid revealing whether the username exists.","triggerScenarios":"Calling LdapAuthenticationProvider/PasswordComparisonAuthenticator.authenticate() when isLdapPasswordCompare() returns false — i.e. the supplied password does not match the user's password attribute (passwordAttributeName, default 'userPassword') per the configured PasswordEncoder, or the one-level bind comparison against the LDAP server fails.","commonSituations":"Wrong password entered by the user; PasswordEncoder mismatch (e.g. LDAP stores SSHA hashes but a plaintext or BCrypt encoder is configured); the LDAP bind comparison fails because the bind DN lacks permission to read the password attribute; typo in user DN/search base so the entry found is not the real user.","solutions":["Verify the user is supplying the correct password (test an ldapwhoami/bind directly with the same DN and password).","Check that the configured PasswordEncoder matches the hash format stored in the password attribute (e.g. LdapShaPasswordEncoder/SSHA vs BCrypt).","Ensure the bind credentials used by the ContextSource have read access to the password attribute if using attribute comparison instead of bind comparison.","Confirm searchFilter/base configuration returns the correct user entry and DN."],"exampleFix":"// before: encoder does not match LDAP storage\nthis.encoder = new BCryptPasswordEncoder();\n// after: use the encoder matching the LDAP password hash scheme, or compare via bind\nauthenticator.setPasswordCompare(new LdapShaPasswordEncoder());\n// or configure the provider to authenticate by LDAP bind instead of attribute comparison","handlingStrategy":"try-catch","validationCode":"// pre-check: bind test before authentication\nDirContext ctx = null;\ntry {\n    ctx = contextSource.getContext(username, rawPassword); // will throw NamingException on bad bind\n} finally {\n    if (ctx != null) LdapUtils.closeContext(ctx);\n}","typeGuard":null,"tryCatchPattern":"try {\n    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));\n} catch (BadCredentialsException e) {\n    // generic message on purpose: show 'Invalid username or password'\n}","preventionTips":["Match the PasswordEncoder to the LDAP password attribute hash format (SSHA/SHA vs BCrypt).","Verify bind DN can read the password attribute when using attribute comparison.","Test credentials directly with ldapwhoami before blaming the application.","Never log the password or the exception details containing DNs."],"tags":["ldap","authentication","bad-credentials","spring-security"],"backgroundTag":"missing-credentials","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}