SEC-1031: LdapShaPasswordEncoder.isPasswordValid startOfHash off by one

http://jira.springframework.org/browse/SEC-1031. Fixed startOfHash value and added tests to check full length of password is used.
This commit is contained in:
Luke Taylor 2008-11-11 23:34:40 +00:00
parent 0ba690fb0e
commit 0bbab88504
2 changed files with 33 additions and 24 deletions

View File

@ -86,9 +86,9 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
sha.update(rawPass.getBytes("UTF-8")); sha.update(rawPass.getBytes("UTF-8"));
} catch (java.security.NoSuchAlgorithmException e) { } catch (java.security.NoSuchAlgorithmException e) {
throw new IllegalStateException("No SHA implementation available!"); throw new IllegalStateException("No SHA implementation available!");
} catch (UnsupportedEncodingException ue) { } catch (UnsupportedEncodingException ue) {
throw new IllegalStateException("UTF-8 not supported!"); throw new IllegalStateException("UTF-8 not supported!");
} }
if (salt != null) { if (salt != null) {
Assert.isInstanceOf(byte[].class, salt, "Salt value must be a byte array"); Assert.isInstanceOf(byte[].class, salt, "Salt value must be a byte array");
@ -131,7 +131,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
*/ */
public boolean isPasswordValid(final String encPass, final String rawPass, Object salt) { public boolean isPasswordValid(final String encPass, final String rawPass, Object salt) {
String prefix = extractPrefix(encPass); String prefix = extractPrefix(encPass);
if (prefix == null) { if (prefix == null) {
return encPass.equals(rawPass); return encPass.equals(rawPass);
} }
@ -141,32 +141,32 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
} else if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) { } else if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {
throw new IllegalArgumentException("Unsupported password prefix '" + prefix + "'"); throw new IllegalArgumentException("Unsupported password prefix '" + prefix + "'");
} else { } else {
// Standard SHA // Standard SHA
salt = null; salt = null;
} }
int startOfHash = prefix.length() + 1; int startOfHash = prefix.length();
String encodedRawPass = encodePassword(rawPass, salt).substring(startOfHash); String encodedRawPass = encodePassword(rawPass, salt).substring(startOfHash);
return encodedRawPass.equals(encPass.substring(startOfHash)); return encodedRawPass.equals(encPass.substring(startOfHash));
} }
/** /**
* Returns the hash prefix or null if there isn't one. * Returns the hash prefix or null if there isn't one.
*/ */
private String extractPrefix(String encPass) { private String extractPrefix(String encPass) {
if (!encPass.startsWith("{")) { if (!encPass.startsWith("{")) {
return null; return null;
} }
int secondBrace = encPass.lastIndexOf('}'); int secondBrace = encPass.lastIndexOf('}');
if (secondBrace < 0) { if (secondBrace < 0) {
throw new IllegalArgumentException("Couldn't find closing brace for SHA prefix"); throw new IllegalArgumentException("Couldn't find closing brace for SHA prefix");
} }
return encPass.substring(0, secondBrace + 1); return encPass.substring(0, secondBrace + 1);
} }
public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) { public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {

View File

@ -44,7 +44,7 @@ public class LdapShaPasswordEncoderTests {
assertFalse(sha.isPasswordValid("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "wrongpassword", null)); assertFalse(sha.isPasswordValid("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "wrongpassword", null));
} }
@Test @Test
public void invalidSaltedPasswordFails() { public void invalidSaltedPasswordFails() {
assertFalse(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "wrongpassword", null)); assertFalse(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "wrongpassword", null));
assertFalse(sha.isPasswordValid("{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "wrongpassword", null)); assertFalse(sha.isPasswordValid("{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "wrongpassword", null));
@ -81,6 +81,15 @@ public class LdapShaPasswordEncoderTests {
assertTrue(sha.isPasswordValid("{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "boabspasswurd", null)); assertTrue(sha.isPasswordValid("{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "boabspasswurd", null));
} }
@Test
// SEC-1031
public void fullLengthOfHashIsUsedInComparison() throws Exception {
// Change the first hash character from '2' to '3'
assertFalse(sha.isPasswordValid("{SSHA}35ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "boabspasswurd", null));
// Change the last hash character from 'X' to 'Y'
assertFalse(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgY", "boabspasswurd", null));
}
@Test @Test
public void correctPrefixCaseIsUsed() { public void correctPrefixCaseIsUsed() {
sha.setForceLowerCasePrefix(false); sha.setForceLowerCasePrefix(false);
@ -95,12 +104,12 @@ public class LdapShaPasswordEncoderTests {
@Test(expected=IllegalArgumentException.class) @Test(expected=IllegalArgumentException.class)
public void invalidPrefixIsRejected() { public void invalidPrefixIsRejected() {
sha.isPasswordValid("{MD9}xxxxxxxxxx" , "somepassword", null); sha.isPasswordValid("{MD9}xxxxxxxxxx" , "somepassword", null);
} }
@Test(expected=IllegalArgumentException.class) @Test(expected=IllegalArgumentException.class)
public void malformedPrefixIsRejected() { public void malformedPrefixIsRejected() {
// No right brace // No right brace
sha.isPasswordValid("{SSHA25ro4PKC8jhQZ26jVsozhX/xaP0suHgX" , "somepassword", null); sha.isPasswordValid("{SSHA25ro4PKC8jhQZ26jVsozhX/xaP0suHgX" , "somepassword", null);
} }
} }