Skip to content

Commit e47647b

Browse files
committed
🦟 fix: Password hash upgrade kills existing passwords
The upgrade of a MD5 stored password hash to a PBKDF password hash destroys the stored password. The has check zeroes out the password that is tested, so that the new hash is built over the zeroed out value. This fix prevents that an also adds a check to the test. Fixes #1335
1 parent 8b18ac3 commit e47647b

File tree

2 files changed

+38
-21
lines changed

2 files changed

+38
-21
lines changed

‎src/main/java/com/gitblit/manager/AuthenticationManager.java

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818
import java.nio.charset.Charset;
1919
import java.security.Principal;
2020
import java.text.MessageFormat;
21-
import java.util.ArrayList;
22-
import java.util.HashMap;
23-
import java.util.List;
24-
import java.util.Map;
21+
import java.util.*;
2522
import java.util.concurrent.TimeUnit;
2623

2724
import javax.servlet.http.Cookie;
@@ -520,21 +517,33 @@ public UserModel authenticate(String username, char[] password, String remoteIP)
520517
protected UserModel authenticateLocal(UserModel user, char [] password) {
521518
UserModel returnedUser = null;
522519

523-
PasswordHash pwdHash = PasswordHash.instanceFor(user.password);
524-
if (pwdHash != null) {
525-
if (pwdHash.matches(user.password, password, user.username)) {
520+
// Create a copy of the password that we can use to rehash to upgrade to a more secure hashing method.
521+
// This is done to be independent from the implementation of the PasswordHash, which might already clear out
522+
// the password it gets passed in. This looks a bit stupid, as we could simply clean up the mess, but this
523+
// falls under "better safe than sorry".
524+
char[] pwdToUpgrade = Arrays.copyOf(password, password.length);
525+
try {
526+
PasswordHash pwdHash = PasswordHash.instanceFor(user.password);
527+
if (pwdHash != null) {
528+
if (pwdHash.matches(user.password, password, user.username)) {
529+
returnedUser = user;
530+
}
531+
} else if (user.password.equals(new String(password))) {
532+
// plain-text password
526533
returnedUser = user;
527534
}
528-
} else if (user.password.equals(new String(password))) {
529-
// plain-text password
530-
returnedUser = user;
531-
}
532-
533-
// validate user
534-
returnedUser = validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);
535-
536-
// try to upgrade the stored password hash to a stronger hash, if necessary
537-
upgradeStoredPassword(returnedUser, password, pwdHash);
535+
536+
// validate user
537+
returnedUser = validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);
538+
539+
// try to upgrade the stored password hash to a stronger hash, if necessary
540+
upgradeStoredPassword(returnedUser, pwdToUpgrade, pwdHash);
541+
}
542+
finally {
543+
// Now we make sure that the password is zeroed out in any case.
544+
Arrays.fill(password, Character.MIN_VALUE);
545+
Arrays.fill(pwdToUpgrade, Character.MIN_VALUE);
546+
}
538547

539548
return returnedUser;
540549
}

‎src/test/java/com/gitblit/tests/AuthenticationManagerTest.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -671,29 +671,37 @@ public void testAuthenticate() throws Exception {
671671
public void testAuthenticateUpgradePlaintext() throws Exception {
672672
IAuthenticationManager auth = newAuthenticationManager();
673673

674+
String password = "topsecret";
674675
UserModel user = new UserModel("sunnyjim");
675-
user.password = "password";
676+
user.password = password;
676677
users.updateUserModel(user);
677678

678-
assertNotNull(auth.authenticate(user.username, user.password.toCharArray(), null));
679+
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));
679680

680681
// validate that plaintext password was automatically updated to hashed one
681682
assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":"));
683+
684+
// validate that the password is still valid and the user can log in
685+
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));
682686
}
683687

684688

685689
@Test
686690
public void testAuthenticateUpgradeMD5() throws Exception {
687691
IAuthenticationManager auth = newAuthenticationManager();
688692

693+
String password = "secretAndHashed";
689694
UserModel user = new UserModel("sunnyjim");
690-
user.password = "MD5:5F4DCC3B5AA765D61D8327DEB882CF99";
695+
user.password = "MD5:BD95A1CFD00868B59B3564112D1E5847";
691696
users.updateUserModel(user);
692697

693-
assertNotNull(auth.authenticate(user.username, "password".toCharArray(), null));
698+
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));
694699

695700
// validate that MD5 password was automatically updated to hashed one
696701
assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":"));
702+
703+
// validate that the password is still valid and the user can log in
704+
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));
697705
}
698706

699707

0 commit comments

Comments
 (0)