1
0
forked from 0ad/0ad

Limit possibility of brute force attacks when guessing password

As suggested by elexis, 1a8de6d2b8 should get some protection against
brute force attacks on password.

This is supposed to prevent attackers from getting connection data by
guessing the password.

Each failed attempt increases the counter.
XmppClient on the server side checks for the users with certain number
of failed attempts, determined in CNetServer, and refuses to check the
password, answering with banned message.
So they cant guess again in given match. Effect of this block will
dissapear after new match is created.

Differential revision: D3467
Comments by: wraitii, Stan
Tested by: Stan, Freagarach
Ref: #5913

This was SVN commit r24794.
This commit is contained in:
Angen 2021-01-26 20:20:48 +00:00
parent 515d34d277
commit 16a91c37e9
5 changed files with 51 additions and 6 deletions

View File

@ -165,6 +165,7 @@ function getConnectionFailReason(reason)
{
case "not_server": return translate("Server is not running.");
case "invalid_password": return translate("Password is invalid.");
case "banned": return translate("You have been banned.");
default:
warn("Unknown connection failure reason: " + reason);
return sprintf(translate("\\[Invalid value %(reason)s]"), { "reason": reason });

View File

@ -942,7 +942,18 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq)
m_client->send(response);
return true;
}
if (!g_NetServer->CheckPassword(CStr(cd->m_Password.c_str())))
if (g_NetServer->IsBanned(iq.from().username()))
{
glooxwrapper::IQ response(gloox::IQ::Result, iq.from(), iq.id());
ConnectionData* connectionData = new ConnectionData();
connectionData->m_Error = "banned";
response.addExtension(connectionData);
m_client->send(response);
return true;
}
if (!g_NetServer->CheckPasswordAndIncrement(CStr(cd->m_Password.c_str()), iq.from().username()))
{
glooxwrapper::IQ response(gloox::IQ::Result, iq.from(), iq.id());
ConnectionData* connectionData = new ConnectionData();

View File

@ -58,6 +58,7 @@
#define DEFAULT_SERVER_NAME L"Unnamed Server"
constexpr int CHANNEL_COUNT = 1;
constexpr int FAILED_PASSWORD_TRIES_BEFORE_BAN = 3;
/**
* enet_host_service timeout (msecs).
@ -1629,9 +1630,26 @@ void CNetServer::SetConnectionData(const CStr& ip, const u16 port, bool useSTUN)
m_UseSTUN = useSTUN;
}
bool CNetServer::CheckPassword(const CStr& password) const
bool CNetServer::CheckPasswordAndIncrement(const CStr& password, const std::string& username)
{
return m_Password == password;
std::unordered_map<std::string, int>::iterator it = m_FailedAttempts.find(username);
if (m_Password == password)
{
if (it != m_FailedAttempts.end())
it->second = 0;
return true;
}
if (it == m_FailedAttempts.end())
m_FailedAttempts.emplace(username, 1);
else
it->second++;
return false;
}
bool CNetServer::IsBanned(const std::string& username) const
{
std::unordered_map<std::string, int>::const_iterator it = m_FailedAttempts.find(username);
return it != m_FailedAttempts.end() && it->second >= FAILED_PASSWORD_TRIES_BEFORE_BAN;
}
void CNetServer::SetPassword(const CStr& password)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -28,6 +28,7 @@
#include <mutex>
#include <string>
#include <utility>
#include <unordered_map>
#include <vector>
#include <thread>
@ -155,7 +156,20 @@ public:
u16 GetPublicPort() const;
bool CheckPassword(const CStr& password) const;
/**
* Check if password is valid. If is not, increase number of failed attempts of the lobby user.
* This is used without established direct session with the client, to prevent brute force attacks
* when guessing password trying to get connection data from the host.
* @return true iff password is valid
*/
bool CheckPasswordAndIncrement(const CStr& password, const std::string& username);
/**
* Check if user reached certain number of failed attempts.
* @see m_BanAfterNumberOfTries
* @see CheckPasswordAndBan
*/
bool IsBanned(const std::string& username) const;
void SetPassword(const CStr& password);
@ -166,6 +180,7 @@ private:
u16 m_PublicPort;
CStr m_PublicIp;
CStr m_Password;
std::unordered_map<std::string, int> m_FailedAttempts;
};
/**

View File

@ -168,7 +168,7 @@ void JSI_Network::StartNetworkJoin(ScriptInterface::CmptPrivate* pCmptPrivate, c
}
}
void JSI_Network::StartNetworkJoinLobby(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, const CStr& hostJID, const CStr& password)
void JSI_Network::StartNetworkJoinLobby(ScriptInterface::CmptPrivate* UNUSED(pCmptPrivate), const CStrW& playerName, const CStr& hostJID, const CStr& password)
{
ENSURE(!!g_XmppClient);
ENSURE(!g_NetClient);