Browse Source

implemented brute force protection

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
devel
Stephan Richter 1 month ago
parent
commit
a10224a23e
  1. 21
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java
  2. 12
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Lock.java
  3. 6
      de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedUserService.java

21
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 static java.util.Optional.empty;
import de.srsoftware.oidc.api.data.AccessToken; 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 de.srsoftware.oidc.api.data.User;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
public interface UserService { public interface UserService {
Map<String, FailedLogin> failedLogins = new HashMap<>(); Map<String, Lock> failedLogins = new HashMap<>();
/** /**
* create a new access token for a given user * create a new access token for a given user
@ -30,25 +30,20 @@ public interface UserService {
public UserService init(User defaultUser); public UserService init(User defaultUser);
public List<User> list(); public List<User> list();
public Set<User> find(String idOrEmail); public Set<User> find(String idOrEmail);
public default Optional<FailedLogin> getLock(String id) { public default Optional<Lock> getLock(String key) {
var failedLogin = failedLogins.get(id); var failedLogin = failedLogins.get(key);
if (failedLogin == null || failedLogin.releaseTime().isBefore(Instant.now())) return empty(); if (failedLogin == null || failedLogin.releaseTime().isBefore(Instant.now())) return empty();
return Optional.of(failedLogin); return Optional.of(failedLogin);
} }
public Optional<User> load(String id); public Optional<User> load(String id);
public Optional<User> login(String username, String password); public Optional<User> login(String username, String password);
public default UserService lock(String id) { public default Lock lock(String key) {
var failedLogin = failedLogins.get(id); return failedLogins.computeIfAbsent(key,k -> new Lock()).count();
if (failedLogin == null) {
failedLogins.put(id, failedLogin = new FailedLogin(id));
}
return this;
} }
public boolean passwordMatches(String plaintextPassword, User user); public boolean passwordMatches(String plaintextPassword, User user);
public UserService save(User user); public UserService save(User user);
public default UserService unlock(String id) { public default UserService unlock(String key) {
failedLogins.remove(id); failedLogins.remove(key);
return this; return this;
} }
public UserService updatePassword(User user, String plaintextPassword); public UserService updatePassword(User user, String plaintextPassword);

12
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/FailedLogin.java → 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; import java.time.Instant;
public class FailedLogin { public class Lock {
private final String userId;
private int attempts; private int attempts;
private Instant releaseTime; private Instant releaseTime;
public FailedLogin(String userId) { public Lock() {
this.userId = userId;
this.attempts = 0; this.attempts = 0;
count();
} }
public void count() { public Lock count() {
attempts++; attempts++;
if (attempts > 13) attempts = 13; if (attempts > 13) attempts = 13;
var seconds = 1; var seconds = 5;
for (long i = 0; i < attempts; i++) seconds *= 2; for (long i = 0; i < attempts; i++) seconds *= 2;
releaseTime = Instant.now().plusSeconds(seconds); releaseTime = Instant.now().plusSeconds(seconds);
return this;
} }
public int attempts() { public int attempts() {

6
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); var optLock = getLock(username);
if (optLock.isPresent()) { if (optLock.isPresent()) {
var lock = optLock.get(); 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(); return empty();
} }
for (var encryptedUser : backend.list()) { for (var encryptedUser : backend.list()) {
@ -109,7 +109,9 @@ public class EncryptedUserService extends EncryptedConfig implements UserService
return Optional.of(decryptedUser); 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(); return empty();
} }

Loading…
Cancel
Save