This is an automated email from the ASF dual-hosted git repository. lprimak pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/shiro.git
commit ecd3abad2a7f663f43d0aab74ff6eded16ebb49f Author: lprimak <[email protected]> AuthorDate: Wed Oct 21 11:34:04 2026 -0500 bugfix: ensure passwordsMatch method returns false for null or empty saved credentials --- .../org/apache/shiro/authc/credential/DefaultPasswordService.java | 6 +++--- .../apache/shiro/authc/credential/SimpleCredentialsMatcher.java | 5 +++++ .../shiro/authc/credential/DefaultPasswordServiceTest.groovy | 8 ++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java b/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java index 95d88038c..4ec9db140 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java @@ -107,7 +107,7 @@ public class DefaultPasswordService implements HashingPasswordService { ByteSource plaintextBytes = createByteSource(plaintext); if (saved == null || saved.isEmpty()) { - return plaintextBytes == null || plaintextBytes.isEmpty(); + return false; } else { if (plaintextBytes == null || plaintextBytes.isEmpty()) { return false; @@ -185,8 +185,8 @@ public class DefaultPasswordService implements HashingPasswordService { public boolean passwordsMatch(Object submittedPlaintext, String saved) { ByteSource plaintextBytes = createByteSource(submittedPlaintext); - if (saved == null || saved.length() == 0) { - return plaintextBytes == null || plaintextBytes.isEmpty(); + if (saved == null || saved.isEmpty()) { + return false; } else { if (plaintextBytes == null || plaintextBytes.isEmpty()) { return false; diff --git a/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java index 9350f0ea0..5073cece6 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java @@ -99,6 +99,11 @@ public class SimpleCredentialsMatcher extends CodecSupport implements Credential * @return {@code true} if the {@code tokenCredentials} are equal to the {@code accountCredentials}. */ protected boolean equals(Object tokenCredentials, Object accountCredentials) { + if (accountCredentials == null + || (isByteSource(accountCredentials) && toBytes(accountCredentials).length == 0)) { + // an empty or missing stored credential never matches anything + return false; + } if (LOGGER.isDebugEnabled() && tokenCredentials != null) { LOGGER.debug("Performing credentials equality check for tokenCredentials of type [" + tokenCredentials.getClass().getName() + " and accountCredentials of type [" diff --git a/core/src/test/groovy/org/apache/shiro/authc/credential/DefaultPasswordServiceTest.groovy b/core/src/test/groovy/org/apache/shiro/authc/credential/DefaultPasswordServiceTest.groovy index 3cf1256ba..ef46ce5fc 100644 --- a/core/src/test/groovy/org/apache/shiro/authc/credential/DefaultPasswordServiceTest.groovy +++ b/core/src/test/groovy/org/apache/shiro/authc/credential/DefaultPasswordServiceTest.groovy @@ -72,10 +72,10 @@ class DefaultPasswordServiceTest { @Test void testPasswordsMatchWithNullOrEmpty() { def service = createSha256Service() - assertTrue service.passwordsMatch(null, (String) null) - assertTrue service.passwordsMatch(null, (Hash) null) - assertTrue service.passwordsMatch("", (String) null) - assertTrue service.passwordsMatch(null, "") + assertFalse service.passwordsMatch(null, (String) null) + assertFalse service.passwordsMatch(null, (Hash) null) + assertFalse service.passwordsMatch("", (String) null) + assertFalse service.passwordsMatch(null, "") assertFalse service.passwordsMatch(null, "12345") assertFalse service.passwordsMatch(null, new Sha384Hash("test")) }
