aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorMatteo Mara <matteo.mara@sonarsource.com>2025-01-07 11:51:27 +0100
committersonartech <sonartech@sonarsource.com>2025-01-09 20:03:23 +0000
commitd6dda575139a485af627fd3d0d0a5a50359ade5c (patch)
treee5c6256987229c4f1fab4c77be1e731d895e3084 /server
parentd047326a1aa1a745cb4e3c0a50f4bda9e745d4e7 (diff)
downloadsonarqube-d6dda575139a485af627fd3d0d0a5a50359ade5c.tar.gz
sonarqube-d6dda575139a485af627fd3d0d0a5a50359ade5c.zip
SONAR-19225 Drop BCRYPT hash method for user passwords
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoIT.java4
-rw-r--r--server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoWithPersisterIT.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java2
-rw-r--r--server/sonar-db-migration/build.gradle1
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaIT.java6
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java10
-rw-r--r--server/sonar-webserver-api/build.gradle1
-rw-r--r--server/sonar-webserver-auth/build.gradle1
-rw-r--r--server/sonar-webserver-auth/src/it/java/org/sonar/server/authentication/CredentialsLocalAuthenticationIT.java62
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsLocalAuthentication.java29
-rw-r--r--server/sonar-webserver-core/build.gradle1
11 files changed, 21 insertions, 98 deletions
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoIT.java
index 18c19baa8f7..33ee933299c 100644
--- a/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoIT.java
+++ b/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoIT.java
@@ -465,7 +465,7 @@ class UserDaoIT {
.setResetPassword(true)
.setSalt("12345")
.setCryptedPassword("abcde")
- .setHashMethod("BCRYPT")
+ .setHashMethod("PBKDF2")
.setExternalLogin("johngithub")
.setExternalIdentityProvider("github")
.setExternalId("EXT_ID")
@@ -485,7 +485,7 @@ class UserDaoIT {
assertThat(reloaded.getSortedScmAccounts()).containsExactly("jo.hn", "john2", "johndoo");
assertThat(reloaded.getSalt()).isEqualTo("12345");
assertThat(reloaded.getCryptedPassword()).isEqualTo("abcde");
- assertThat(reloaded.getHashMethod()).isEqualTo("BCRYPT");
+ assertThat(reloaded.getHashMethod()).isEqualTo("PBKDF2");
assertThat(reloaded.getExternalLogin()).isEqualTo("johngithub");
assertThat(reloaded.getExternalIdentityProvider()).isEqualTo("github");
assertThat(reloaded.getExternalId()).isEqualTo("EXT_ID");
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoWithPersisterIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoWithPersisterIT.java
index 474f5e4760c..cdb44c43719 100644
--- a/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoWithPersisterIT.java
+++ b/server/sonar-db-dao/src/it/java/org/sonar/db/user/UserDaoWithPersisterIT.java
@@ -92,7 +92,7 @@ class UserDaoWithPersisterIT {
.setResetPassword(true)
.setSalt("12345")
.setCryptedPassword("abcde")
- .setHashMethod("BCRYPT")
+ .setHashMethod("PBKDF2")
.setExternalLogin("johngithub")
.setExternalIdentityProvider("github")
.setExternalId("EXT_ID")
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java
index 0bcfcf15c6a..a6e4fab242c 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java
@@ -43,7 +43,7 @@ public class UserDto implements UserId {
private String externalIdentityProvider;
// Hashed password that may be null in case of external authentication
private String cryptedPassword;
- // Salt used for PBKDF2, null when bcrypt is used or for external authentication
+ // Null for external authentication
private String salt;
// Hash method used to generate cryptedPassword, my be null in case of external authentication
private String hashMethod;
diff --git a/server/sonar-db-migration/build.gradle b/server/sonar-db-migration/build.gradle
index 9b5a4032e13..7e51703034d 100644
--- a/server/sonar-db-migration/build.gradle
+++ b/server/sonar-db-migration/build.gradle
@@ -28,7 +28,6 @@ dependencies {
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation 'org.junit.jupiter:junit-jupiter-params'
- testImplementation 'org.mindrot:jbcrypt'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.xmlunit:xmlunit-core'
testImplementation 'org.xmlunit:xmlunit-matchers'
diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaIT.java
index d0de5241b0f..8cd32fb3219 100644
--- a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaIT.java
+++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaIT.java
@@ -103,14 +103,14 @@ class PopulateInitialSchemaIT {
.containsEntry("EXTERNAL_LOGIN", "admin")
.containsEntry("EXT_IDENT_PROVIDER", "sonarqube")
.containsEntry("USER_LOCAL", true)
- .containsEntry("CRYPTED_PASSWORD", "$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi")
- .containsEntry("HASH_METHOD", "BCRYPT")
+ .containsEntry("CRYPTED_PASSWORD", "100000$R9xDN18ebKxA3ZTaputi6wDt+fcKhP2h3GgAjGbcBlCSlkMLENxw9wziHS46QIW3fWOjEMpeyEts+pNuPXSbYA==")
+ .containsEntry("SALT", "pSDhsn3IM3KCa74CRRf7T7Vx+OE=")
+ .containsEntry("HASH_METHOD", "PBKDF2")
.containsEntry("CREATED_AT", NOW)
.containsEntry("RESET_PASSWORD", true)
.containsEntry("UPDATED_AT", NOW);
assertThat(cols.get("EMAIL")).isNull();
- assertThat(cols.get("SALT")).isNull();
}
private void verifyGroup(String expectedName, String expectedDescription) {
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java
index d4ada1e3561..67a25b6dbbd 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java
@@ -42,7 +42,8 @@ public class PopulateInitialSchema extends DataChange {
private static final String ADMINS_GROUP = "sonar-administrators";
private static final String USERS_GROUP = "sonar-users";
private static final String ADMIN_USER = "admin";
- private static final String ADMIN_CRYPTED_PASSWORD = "$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi";
+ private static final String ADMIN_CRYPTED_PASSWORD = "100000$R9xDN18ebKxA3ZTaputi6wDt+fcKhP2h3GgAjGbcBlCSlkMLENxw9wziHS46QIW3fWOjEMpeyEts+pNuPXSbYA==";
+ private static final String ADMIN_SALT = "pSDhsn3IM3KCa74CRRf7T7Vx+OE=";
private static final List<String> ADMIN_ROLES = Arrays.asList("admin", "profileadmin", "gateadmin", "provisioning", "applicationcreator", "portfoliocreator");
private final System2 system2;
@@ -75,14 +76,15 @@ public class PopulateInitialSchema extends DataChange {
"(uuid, login, name, email, external_id, external_login, external_identity_provider, user_local, crypted_password, salt, hash_method, reset_password, " +
"created_at, updated_at)" +
" values " +
- "(?, ?, 'Administrator', null, 'admin', 'admin', 'sonarqube', ?, ?, null, 'BCRYPT', ?, ?, ?)")
+ "(?, ?, 'Administrator', null, 'admin', 'admin', 'sonarqube', ?, ?, ?, 'PBKDF2', ?, ?, ?)")
.setString(1, uuidFactory.create())
.setString(2, ADMIN_USER)
.setBoolean(3, true)
.setString(4, ADMIN_CRYPTED_PASSWORD)
- .setBoolean(5, true)
- .setLong(6, now)
+ .setString(5, ADMIN_SALT)
+ .setBoolean(6, true)
.setLong(7, now)
+ .setLong(8, now)
.execute()
.commit();
diff --git a/server/sonar-webserver-api/build.gradle b/server/sonar-webserver-api/build.gradle
index adae50c4454..01364a1bf3c 100644
--- a/server/sonar-webserver-api/build.gradle
+++ b/server/sonar-webserver-api/build.gradle
@@ -25,7 +25,6 @@ dependencies {
api project(':server:sonar-process')
api project(':server:sonar-server-common')
api project(':sonar-plugin-api-impl')
- api 'org.mindrot:jbcrypt'
compileOnlyApi 'com.github.spotbugs:spotbugs-annotations'
compileOnlyApi 'jakarta.servlet:jakarta.servlet-api'
diff --git a/server/sonar-webserver-auth/build.gradle b/server/sonar-webserver-auth/build.gradle
index 121114d1d8a..b6b042a8eee 100644
--- a/server/sonar-webserver-auth/build.gradle
+++ b/server/sonar-webserver-auth/build.gradle
@@ -18,7 +18,6 @@ dependencies {
api project(':server:sonar-webserver-api')
api project(':sonar-plugin-api-impl')
api project(':server:sonar-auth-ldap')
- api 'org.mindrot:jbcrypt'
compileOnlyApi 'com.github.spotbugs:spotbugs-annotations'
compileOnlyApi 'jakarta.servlet:jakarta.servlet-api'
diff --git a/server/sonar-webserver-auth/src/it/java/org/sonar/server/authentication/CredentialsLocalAuthenticationIT.java b/server/sonar-webserver-auth/src/it/java/org/sonar/server/authentication/CredentialsLocalAuthenticationIT.java
index 57986e45475..d559d6a9778 100644
--- a/server/sonar-webserver-auth/src/it/java/org/sonar/server/authentication/CredentialsLocalAuthenticationIT.java
+++ b/server/sonar-webserver-auth/src/it/java/org/sonar/server/authentication/CredentialsLocalAuthenticationIT.java
@@ -21,13 +21,11 @@ package org.sonar.server.authentication;
import java.security.SecureRandom;
import java.util.Base64;
-import java.util.Optional;
import java.util.Random;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.mindrot.jbcrypt.BCrypt;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
@@ -41,7 +39,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.sonar.db.user.UserTesting.newUserDto;
-import static org.sonar.server.authentication.CredentialsLocalAuthentication.HashMethod.BCRYPT;
import static org.sonar.server.authentication.CredentialsLocalAuthentication.HashMethod.PBKDF2;
public class CredentialsLocalAuthenticationIT {
@@ -84,17 +81,6 @@ public class CredentialsLocalAuthenticationIT {
}
@Test
- public void authentication_with_bcrypt_with_correct_password_should_work() {
- String password = secure().nextAlphanumeric(60);
-
- UserDto user = newUserDto()
- .setHashMethod(BCRYPT.name())
- .setCryptedPassword(BCrypt.hashpw(password, BCrypt.gensalt(12)));
-
- underTest.authenticate(db.getSession(), user, password, AuthenticationEvent.Method.BASIC);
- }
-
- @Test
public void authentication_with_sha1_should_throw_AuthenticationException() {
String password = secure().nextAlphanumeric(60);
@@ -114,51 +100,17 @@ public class CredentialsLocalAuthenticationIT {
}
@Test
- public void authentication_with_bcrypt_with_incorrect_password_should_throw_AuthenticationException() {
- DbSession dbSession = db.getSession();
- String password = secure().nextAlphanumeric(60);
-
- UserDto user = newUserDto()
- .setHashMethod(BCRYPT.name())
- .setCryptedPassword(BCrypt.hashpw(password, BCrypt.gensalt(12)));
-
- assertThatThrownBy(() -> underTest.authenticate(dbSession, user, "WHATEVER", AuthenticationEvent.Method.BASIC))
- .isInstanceOf(AuthenticationException.class)
- .hasMessage(CredentialsLocalAuthentication.ERROR_WRONG_PASSWORD);
- }
-
- @Test
- public void authentication_with_bcrypt_with_empty_password_should_throw_AuthenticationException() {
- DbSession dbSession = db.getSession();
- UserDto user = newUserDto()
- .setCryptedPassword(null)
- .setHashMethod(BCRYPT.name());
-
- assertThatThrownBy(() -> underTest.authenticate(dbSession, user, "WHATEVER", AuthenticationEvent.Method.BASIC))
- .isInstanceOf(AuthenticationException.class)
- .hasMessage(CredentialsLocalAuthentication.ERROR_NULL_PASSWORD_IN_DB);
- }
-
- @Test
- public void authentication_upgrade_hash_function_when_BCRYPT_was_used() {
+ public void authentication_with_bcrypt_should_throw_AuthenticationException() {
String password = secure().nextAlphanumeric(60);
UserDto user = newUserDto()
- .setLogin("myself")
- .setHashMethod(BCRYPT.name())
- .setCryptedPassword(BCrypt.hashpw(password, BCrypt.gensalt(12)))
- .setSalt(null);
- db.users().insertUser(user);
-
- underTest.authenticate(db.getSession(), user, password, AuthenticationEvent.Method.BASIC);
-
- Optional<UserDto> myself = db.users().selectUserByLogin("myself");
- assertThat(myself).isPresent();
- assertThat(myself.get().getHashMethod()).isEqualTo(PBKDF2.name());
- assertThat(myself.get().getSalt()).isNotNull();
+ .setHashMethod("BCRYPT")
+ .setCryptedPassword(password);
- // authentication must work with upgraded hash method
- underTest.authenticate(db.getSession(), user, password, AuthenticationEvent.Method.BASIC);
+ DbSession session = db.getSession();
+ assertThatExceptionOfType(AuthenticationException.class)
+ .isThrownBy(() -> underTest.authenticate(session, user, password, AuthenticationEvent.Method.BASIC))
+ .withMessage("Unknown hash method [BCRYPT]");
}
@Test
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsLocalAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsLocalAuthentication.java
index b63be030b60..7fdb174d2bc 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsLocalAuthentication.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsLocalAuthentication.java
@@ -28,7 +28,6 @@ import javax.annotation.Nullable;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.apache.commons.lang3.RandomStringUtils;
-import org.mindrot.jbcrypt.BCrypt;
import org.sonar.api.config.Configuration;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -59,12 +58,11 @@ public class CredentialsLocalAuthentication {
private final EnumMap<HashMethod, HashFunction> hashFunctions = new EnumMap<>(HashMethod.class);
public enum HashMethod {
- BCRYPT, PBKDF2
+ PBKDF2
}
public CredentialsLocalAuthentication(DbClient dbClient, Configuration configuration) {
this.dbClient = dbClient;
- hashFunctions.put(HashMethod.BCRYPT, new BcryptFunction());
hashFunctions.put(HashMethod.PBKDF2, new PBKDF2Function(configuration.getInt(PBKDF2_ITERATIONS_PROP).orElse(null)));
}
@@ -244,29 +242,4 @@ public class CredentialsLocalAuthentication {
}
}
}
-
- /**
- * Implementation of deprecated bcrypt hash function
- */
- private static final class BcryptFunction implements HashFunction {
- @Override
- public AuthenticationResult checkCredentials(UserDto user, String password) {
- if (user.getCryptedPassword() == null) {
- return new AuthenticationResult(false, ERROR_NULL_PASSWORD_IN_DB);
- }
- // This behavior is overridden in most of integration tests for performance reasons, any changes to BCrypt calls should be propagated to
- // Byteman classes
- if (!BCrypt.checkpw(password, user.getCryptedPassword())) {
- return new AuthenticationResult(false, ERROR_WRONG_PASSWORD);
- }
- return new AuthenticationResult(true, "");
- }
-
- @Override
- public void storeHashPassword(UserDto user, String password) {
- user.setHashMethod(HashMethod.BCRYPT.name())
- .setCryptedPassword(BCrypt.hashpw(password, BCrypt.gensalt(12)))
- .setSalt(null);
- }
- }
}
diff --git a/server/sonar-webserver-core/build.gradle b/server/sonar-webserver-core/build.gradle
index d178f7c7fbb..63f2814ea6a 100644
--- a/server/sonar-webserver-core/build.gradle
+++ b/server/sonar-webserver-core/build.gradle
@@ -32,7 +32,6 @@ dependencies {
api 'org.slf4j:slf4j-api'
api 'org.sonarsource.api.plugin:sonar-plugin-api'
api 'org.sonarsource.update-center:sonar-update-center-common'
- api 'org.mindrot:jbcrypt'
api project(':server:sonar-ce-common')
api project(':server:sonar-ce-task')