1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 00:22:50 -05:00

[Captcha] Implement failed logins ceiling (#1870)

* [Hacker1] Failed Login Attempts Captcha

* [Captcha] Implement failed logins ceiling

* Formatting

* Updated approach after implementation talks with Kyle

* Updated email templates // Updated calling arch for failed attempts

* Formatting

* Updated 2fa email links

* Renamed baserequest methods to better match their actions

* EF migrations/scripts

* Updated with requested changes

* Defaults for MaxiumumFailedLoginAttempts
This commit is contained in:
Vincent Salucci
2022-03-02 15:45:00 -06:00
committed by GitHub
parent 7bdb07da93
commit 19d5817f8f
30 changed files with 3669 additions and 19 deletions

View File

@ -83,8 +83,19 @@ namespace Bit.Core.Services
return root.GetProperty("success").GetBoolean();
}
public bool RequireCaptchaValidation(ICurrentContext currentContext) =>
currentContext.IsBot || _globalSettings.Captcha.ForceCaptchaRequired;
public bool RequireCaptchaValidation(ICurrentContext currentContext, int? failedLoginCount = null)
{
var failedLoginCeiling = _globalSettings.Captcha.MaximumFailedLoginAttempts.GetValueOrDefault();
return currentContext.IsBot ||
_globalSettings.Captcha.ForceCaptchaRequired ||
failedLoginCeiling > 0 && failedLoginCount.GetValueOrDefault() >= failedLoginCeiling;
}
public bool ValidateFailedAuthEmailConditions(bool unknownDevice, int failedLoginCount)
{
var failedLoginCeiling = _globalSettings.Captcha.MaximumFailedLoginAttempts.GetValueOrDefault();
return unknownDevice && failedLoginCeiling > 0 && failedLoginCount == failedLoginCeiling;
}
private static bool TokenIsApiKey(string bypassToken, User user) =>
!string.IsNullOrWhiteSpace(bypassToken) && user != null && user.ApiKey == bypassToken;

View File

@ -874,5 +874,39 @@ namespace Bit.Core.Services
message.Category = "OTP";
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendFailedLoginAttemptsEmailAsync(string email, DateTime utcNow, string ip)
{
var message = CreateDefaultMessage("Failed login attempts detected", email);
var model = new FailedAuthAttemptsModel()
{
TheDate = utcNow.ToLongDateString(),
TheTime = utcNow.ToShortTimeString(),
TimeZone = "UTC",
IpAddress = ip,
AffectedEmail = email
};
await AddMessageContentAsync(message, "FailedLoginAttempts", model);
message.Category = "FailedLoginAttempts";
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendFailedTwoFactorAttemptsEmailAsync(string email, DateTime utcNow, string ip)
{
var message = CreateDefaultMessage("Failed login attempts detected", email);
var model = new FailedAuthAttemptsModel()
{
TheDate = utcNow.ToLongDateString(),
TheTime = utcNow.ToShortTimeString(),
TimeZone = "UTC",
IpAddress = ip,
AffectedEmail = email
};
await AddMessageContentAsync(message, "FailedTwoFactorAttempts", model);
message.Category = "FailedTwoFactorAttempts";
await _mailDeliveryService.SendEmailAsync(message);
}
}
}