mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-27 08:24:39 -07:00
[Security] Add per-address rate limiting for auth endpoints
Introduce a thread-safe RateLimiter that tracks attempts per key (IP address) within a sliding time window, and wire it into the authentication endpoints: - Login: failed login attempts from an address are counted; once the configured maximum is exceeded within the window, further logins from that address are rejected with RespTooManyRequests. A successful login clears the failed attempts for that address. - Registration: implement the previously stubbed tooManyRegistrationAttempts, limiting how many accounts can be created per address per window. - Forgot-password: throttle both the email-request and the email-challenge paths per address. New [security] settings with defaults: max_login_attempts_per_ip=5 / login_attempt_window_seconds=900 max_registrations_per_ip=2 / registration_window_seconds=3600 max_forgot_password_requests_per_ip=3 / forgot_password_window_seconds=3600 Adds unit tests for the RateLimiter (window limit, over-limit blocking, clearing, per-key independence). Took 3 minutes
This commit is contained in:
parent
1ed9823b56
commit
b2f63255f0
11 changed files with 238 additions and 3 deletions
|
|
@ -175,6 +175,15 @@ public:
|
|||
{
|
||||
return false;
|
||||
}
|
||||
/** @brief Record a failed login attempt from the given address; returns true if the address is now locked out. */
|
||||
virtual bool recordFailedLogin(const QString & /*ipAddress*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/** @brief Clear any failed-login lockout for the given address, e.g. after a successful login. */
|
||||
virtual void clearFailedLogins(const QString & /*ipAddress*/)
|
||||
{
|
||||
}
|
||||
|
||||
Server_DatabaseInterface *getDatabaseInterface() const;
|
||||
int getNextLocalGameId()
|
||||
|
|
|
|||
|
|
@ -527,10 +527,14 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
return Response::RespUserIsBanned;
|
||||
}
|
||||
case NotLoggedIn:
|
||||
if (server->recordFailedLogin(getAddress())) {
|
||||
return Response::RespTooManyRequests;
|
||||
}
|
||||
return Response::RespWrongPassword;
|
||||
case WouldOverwriteOldSession:
|
||||
return Response::RespWouldOverwriteOldSession;
|
||||
case UsernameInvalid: {
|
||||
server->recordFailedLogin(getAddress());
|
||||
auto *re = new Response_Login;
|
||||
re->set_denied_reason_str(reasonStr.toStdString());
|
||||
rc.setResponseExtension(re);
|
||||
|
|
@ -541,8 +545,10 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
case ClientIdRequired:
|
||||
return Response::RespClientIdRequired;
|
||||
case UserIsInactive:
|
||||
server->recordFailedLogin(getAddress());
|
||||
return Response::RespAccountNotActivated;
|
||||
default:
|
||||
server->clearFailedLogins(getAddress());
|
||||
authState = res;
|
||||
usingRealPassword = needsHash;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue