Skip to content

Commit d36b263

Browse files
committed
prevent password brute force (fusion32#4)
Prevent blocked IP addresses and accounts from returning anything other than a blocked error message. Previously, further attempts would first check the password which enabled brute force attacks to use the "invalid account/password" message as an oracle.
1 parent 6aa1339 commit d36b263

1 file changed

Lines changed: 24 additions & 18 deletions

File tree

src/query.cc

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -518,17 +518,19 @@ static void CheckAccountPasswordTx(TDatabase *Database, TQuery *Query,
518518
TransactionScope Tx("CheckAccountPassword");
519519
QUERY_STOP_IF(!Tx.Begin(Database));
520520

521+
// IMPORTANT(fusion): Disallow blocked IP addresses and accounts early to
522+
// prevent error messages from being used to brute force passwords.
523+
int FailedLoginAttempts;
524+
QUERY_STOP_IF(!GetIPAddressFailedLoginAttempts(Database, IPAddress, 30 * 60, &FailedLoginAttempts));
525+
QUERY_ERROR_IF(FailedLoginAttempts > 20, 4);
526+
QUERY_STOP_IF(!GetAccountFailedLoginAttempts(Database, AccountID, 5 * 60, &FailedLoginAttempts));
527+
QUERY_ERROR_IF(FailedLoginAttempts > 10, 3);
528+
521529
TAccount Account;
522530
QUERY_STOP_IF(!GetAccountData(Database, AccountID, &Account));
523531
QUERY_ERROR_IF(Account.AccountID == 0, 1);
524532
QUERY_ERROR_IF(!TestPassword(Account.Auth, sizeof(Account.Auth), Password), 2);
525533

526-
int FailedLoginAttempts;
527-
QUERY_STOP_IF(!GetAccountFailedLoginAttempts(Database, Account.AccountID, 5 * 60, &FailedLoginAttempts));
528-
QUERY_ERROR_IF(FailedLoginAttempts > 10, 3);
529-
QUERY_STOP_IF(!GetIPAddressFailedLoginAttempts(Database, IPAddress, 30 * 60, &FailedLoginAttempts));
530-
QUERY_ERROR_IF(FailedLoginAttempts > 20, 4);
531-
532534
QUERY_STOP_IF(!Tx.Commit());
533535
QueryOk(Query);
534536
}
@@ -558,17 +560,19 @@ static void LoginAccountTx(TDatabase *Database, TQuery *Query,
558560
TransactionScope Tx("LoginAccount");
559561
QUERY_STOP_IF(!Tx.Begin(Database));
560562

563+
// IMPORTANT(fusion): Disallow blocked IP addresses and accounts early to
564+
// prevent error messages from being used to brute force passwords.
565+
int FailedLoginAttempts;
566+
QUERY_STOP_IF(!GetIPAddressFailedLoginAttempts(Database, IPAddress, 30 * 60, &FailedLoginAttempts));
567+
QUERY_ERROR_IF(FailedLoginAttempts > 20, 4);
568+
QUERY_STOP_IF(!GetAccountFailedLoginAttempts(Database, AccountID, 5 * 60, &FailedLoginAttempts));
569+
QUERY_ERROR_IF(FailedLoginAttempts > 10, 3);
570+
561571
TAccount Account;
562572
QUERY_STOP_IF(!GetAccountData(Database, AccountID, &Account));
563573
QUERY_ERROR_IF(Account.AccountID == 0, 1);
564574
QUERY_ERROR_IF(!TestPassword(Account.Auth, sizeof(Account.Auth), Password), 2);
565575

566-
int FailedLoginAttempts;
567-
QUERY_STOP_IF(!GetAccountFailedLoginAttempts(Database, Account.AccountID, 5 * 60, &FailedLoginAttempts));
568-
QUERY_ERROR_IF(FailedLoginAttempts > 10, 3);
569-
QUERY_STOP_IF(!GetIPAddressFailedLoginAttempts(Database, IPAddress, 30 * 60, &FailedLoginAttempts));
570-
QUERY_ERROR_IF(FailedLoginAttempts > 20, 4);
571-
572576
bool IsBanished;
573577
QUERY_STOP_IF(!IsAccountBanished(Database, Account.AccountID, &IsBanished));
574578
QUERY_ERROR_IF(IsBanished, 5);
@@ -628,6 +632,14 @@ static void LoginGameTx(TDatabase *Database, TQuery *Query,
628632
TransactionScope Tx("LoginGame");
629633
QUERY_STOP_IF(!Tx.Begin(Database));
630634

635+
// IMPORTANT(fusion): Disallow blocked IP addresses and accounts early to
636+
// prevent error messages from being used to brute force passwords.
637+
int FailedLoginAttempts;
638+
QUERY_STOP_IF(!GetIPAddressFailedLoginAttempts(Database, IPAddress, 30 * 60, &FailedLoginAttempts));
639+
QUERY_ERROR_IF(FailedLoginAttempts > 20, 9);
640+
QUERY_STOP_IF(!GetAccountFailedLoginAttempts(Database, AccountID, 5 * 60, &FailedLoginAttempts));
641+
QUERY_ERROR_IF(FailedLoginAttempts > 10, 7);
642+
631643
TCharacterLoginData Character;
632644
QUERY_STOP_IF(!GetCharacterLoginData(Database, CharacterName, &Character));
633645
QUERY_ERROR_IF(Character.CharacterID == 0, 1);
@@ -646,12 +658,6 @@ static void LoginGameTx(TDatabase *Database, TQuery *Query,
646658
QUERY_ERROR_IF(Account.Deleted, 8);
647659
QUERY_ERROR_IF(!TestPassword(Account.Auth, sizeof(Account.Auth), Password), 6);
648660

649-
int FailedLoginAttempts;
650-
QUERY_STOP_IF(!GetAccountFailedLoginAttempts(Database, Account.AccountID, 5 * 60, &FailedLoginAttempts));
651-
QUERY_ERROR_IF(FailedLoginAttempts > 10, 7);
652-
QUERY_STOP_IF(!GetIPAddressFailedLoginAttempts(Database, IPAddress, 30 * 60, &FailedLoginAttempts));
653-
QUERY_ERROR_IF(FailedLoginAttempts > 20, 9);
654-
655661
bool IsBanished;
656662
QUERY_STOP_IF(!IsAccountBanished(Database, Account.AccountID, &IsBanished));
657663
QUERY_ERROR_IF(IsBanished, 10);

0 commit comments

Comments
 (0)