Possible overflow in brute force computation

closes #30939

Signed-off-by: mposolda <mposolda@gmail.com>
(cherry picked from commit a2cc51aed7692ec09c619f2a6f4ecc7055beb9e1)
This commit is contained in:
mposolda 2025-10-15 15:48:19 +02:00 committed by Marek Posolda
parent a752492843
commit a794fca977
3 changed files with 42 additions and 9 deletions

View File

@ -89,12 +89,12 @@ public class DefaultBruteForceProtector implements BruteForceProtector {
userLoginFailure.incrementFailures();
logger.debugv("new num failures: {0}", userLoginFailure.getNumFailures());
int waitSeconds = 0;
if(!(realm.isPermanentLockout() && realm.getMaxTemporaryLockouts() == 0)) {
if(RealmRepresentation.BruteForceStrategy.MULTIPLE.equals(realm.getBruteForceStrategy())) {
waitSeconds = realm.getWaitIncrementSeconds() * (userLoginFailure.getNumFailures() / realm.getFailureFactor());
long waitSeconds = 0L;
if (!(realm.isPermanentLockout() && realm.getMaxTemporaryLockouts() == 0)) {
if (RealmRepresentation.BruteForceStrategy.MULTIPLE.equals(realm.getBruteForceStrategy())) {
waitSeconds = (long) realm.getWaitIncrementSeconds() * ((long) userLoginFailure.getNumFailures() / realm.getFailureFactor());
} else {
waitSeconds = realm.getWaitIncrementSeconds() * (1 + userLoginFailure.getNumFailures() - realm.getFailureFactor());
waitSeconds = (long) realm.getWaitIncrementSeconds() * ((long) 1 + userLoginFailure.getNumFailures() - realm.getFailureFactor());
}
}
@ -116,10 +116,12 @@ public class DefaultBruteForceProtector implements BruteForceProtector {
if (!quickLoginFailure) {
userLoginFailure.incrementTemporaryLockouts();
}
if(quickLoginFailure || !realm.isPermanentLockout() || userLoginFailure.getNumTemporaryLockouts() <= realm.getMaxTemporaryLockouts()) {
int notBefore = (int) (failureTime / 1000) + waitSeconds;
if (quickLoginFailure || !realm.isPermanentLockout() || userLoginFailure.getNumTemporaryLockouts() <= realm.getMaxTemporaryLockouts()) {
long notBefore = (failureTime / 1000) + waitSeconds;
logger.debugv("set notBefore: {0}", notBefore);
userLoginFailure.setFailedLoginNotBefore(notBefore);
// Converting to int is workaround for the fact that "failedLoginNotBefore" is int in the model. Should be fine as user would be considered temporarily disabled with Integer.MAX_VALUE
int notBeforeInt = notBefore > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) notBefore;
userLoginFailure.setFailedLoginNotBefore(notBeforeInt);
sendEvent(session, realm, userLoginFailure, EventType.USER_DISABLED_BY_TEMPORARY_LOCKOUT);
}
}
@ -223,7 +225,7 @@ public class DefaultBruteForceProtector implements BruteForceProtector {
UserLoginFailureModel userLoginFailure = getUserFailureModel(session, realm, user.getId());
if (userLoginFailure != null) {
int currTime = (int) (Time.currentTimeMillis() / 1000);
long currTime = Time.currentTimeMillis() / 1000;
int failedLoginNotBefore = userLoginFailure.getFailedLoginNotBefore();
if (currTime < failedLoginNotBefore) {
logger.debugv("Current: {0} notBefore: {1}", currTime, failedLoginNotBefore);

View File

@ -105,6 +105,21 @@ public class RealmAttributeUpdater extends ServerResourceUpdater<RealmAttributeU
return this;
}
public RealmAttributeUpdater setWaitIncrementSeconds(Integer value) {
rep.setWaitIncrementSeconds(value);
return this;
}
public RealmAttributeUpdater setMaxFailureWaitSeconds(Integer value) {
rep.setMaxFailureWaitSeconds(value);
return this;
}
public RealmAttributeUpdater setMaxDeltaTimeSeconds(Integer value) {
rep.setMaxDeltaTimeSeconds(value);
return this;
}
public RealmAttributeUpdater setEventsListeners(List<String> eventListanets) {
rep.setEventsListeners(eventListanets);
return this;

View File

@ -470,6 +470,22 @@ public class BruteForceTest extends AbstractChangeImportedUserPasswordsTest {
loginSuccess();
}
// Issue 30939
@Test
public void testNoOverflowDuringBruteForceCalculation() throws Exception {
int waitTime = Integer.MAX_VALUE - 172800; // Max int value without 2 days
try (RealmAttributeUpdater updater = new RealmAttributeUpdater(testRealm())
.setWaitIncrementSeconds(waitTime)
.setMaxFailureWaitSeconds(waitTime)
.setMaxDeltaTimeSeconds(900) // 15 minutes
.update()) {
loginInvalidPassword("test-user@localhost");
loginInvalidPassword("test-user@localhost");
expectTemporarilyDisabled();
}
}
@Test
public void testByMultipleStrategy() throws Exception {