From a10224a23ec61ad25bf8bfc8a629acadd0c63298 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Fri, 18 Oct 2024 13:57:43 +0200 Subject: [PATCH] implemented brute force protection Signed-off-by: Stephan Richter --- .../de/srsoftware/oidc/api/UserService.java | 21 +++++++------------ .../api/data/{FailedLogin.java => Lock.java} | 12 +++++------ .../encrypted/EncryptedUserService.java | 6 ++++-- 3 files changed, 17 insertions(+), 22 deletions(-) rename de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/{FailedLogin.java => Lock.java} (71%) diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java index 4ca1996..4331d63 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java @@ -4,13 +4,13 @@ package de.srsoftware.oidc.api; import static java.util.Optional.empty; import de.srsoftware.oidc.api.data.AccessToken; -import de.srsoftware.oidc.api.data.FailedLogin; +import de.srsoftware.oidc.api.data.Lock; import de.srsoftware.oidc.api.data.User; import java.time.Instant; import java.util.*; public interface UserService { - Map failedLogins = new HashMap<>(); + Map failedLogins = new HashMap<>(); /** * create a new access token for a given user @@ -30,25 +30,20 @@ public interface UserService { public UserService init(User defaultUser); public List list(); public Set find(String idOrEmail); - public default Optional getLock(String id) { - var failedLogin = failedLogins.get(id); + public default Optional getLock(String key) { + var failedLogin = failedLogins.get(key); if (failedLogin == null || failedLogin.releaseTime().isBefore(Instant.now())) return empty(); return Optional.of(failedLogin); } public Optional load(String id); public Optional login(String username, String password); - public default UserService lock(String id) { - var failedLogin = failedLogins.get(id); - if (failedLogin == null) { - failedLogins.put(id, failedLogin = new FailedLogin(id)); - } - - return this; + public default Lock lock(String key) { + return failedLogins.computeIfAbsent(key,k -> new Lock()).count(); } public boolean passwordMatches(String plaintextPassword, User user); public UserService save(User user); - public default UserService unlock(String id) { - failedLogins.remove(id); + public default UserService unlock(String key) { + failedLogins.remove(key); return this; } public UserService updatePassword(User user, String plaintextPassword); diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/FailedLogin.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Lock.java similarity index 71% rename from de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/FailedLogin.java rename to de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Lock.java index 1550cdd..50803ed 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/FailedLogin.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Lock.java @@ -3,23 +3,21 @@ package de.srsoftware.oidc.api.data; import java.time.Instant; -public class FailedLogin { - private final String userId; +public class Lock { private int attempts; private Instant releaseTime; - public FailedLogin(String userId) { - this.userId = userId; + public Lock() { this.attempts = 0; - count(); } - public void count() { + public Lock count() { attempts++; if (attempts > 13) attempts = 13; - var seconds = 1; + var seconds = 5; for (long i = 0; i < attempts; i++) seconds *= 2; releaseTime = Instant.now().plusSeconds(seconds); + return this; } public int attempts() { diff --git a/de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedUserService.java b/de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedUserService.java index 8a0af78..85b0997 100644 --- a/de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedUserService.java +++ b/de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedUserService.java @@ -98,7 +98,7 @@ public class EncryptedUserService extends EncryptedConfig implements UserService var optLock = getLock(username); if (optLock.isPresent()) { var lock = optLock.get(); - LOG.log(WARNING, "{} is locked after {} failed logins. Lock will be released at {}", username, lock.attempts(), lock.releaseTime()); + LOG.log(WARNING, "{0} is locked after {1} failed logins. Lock will be released at {2}", username, lock.attempts(), lock.releaseTime()); return empty(); } for (var encryptedUser : backend.list()) { @@ -109,7 +109,9 @@ public class EncryptedUserService extends EncryptedConfig implements UserService return Optional.of(decryptedUser); } } - lock(username); + + var lock = lock(username); + LOG.log(WARNING,"Login failed for {0} → locking account until {1}",username,lock.releaseTime()); return empty(); }