1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-04 17:42:49 -05:00

captcha scores (#1967)

* captcha scores

* some api fixes

* check bot on captcha attribute

* Update src/Core/Services/Implementations/HCaptchaValidationService.cs

Co-authored-by: e271828- <e271828-@users.noreply.github.com>

Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com>
Co-authored-by: e271828- <e271828-@users.noreply.github.com>
This commit is contained in:
Kyle Spearrin
2022-05-09 12:25:13 -04:00
committed by GitHub
parent a5bfc0554b
commit 3ffd240287
10 changed files with 138 additions and 66 deletions

View File

@ -12,6 +12,7 @@ using Bit.Core.Enums;
using Bit.Core.Identity;
using Bit.Core.Models;
using Bit.Core.Models.Api;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Services;
@ -78,18 +79,25 @@ namespace Bit.Core.IdentityServer
_captchaValidationService = captchaValidationService;
}
protected async Task ValidateAsync(T context, ValidatedTokenRequest request, bool unknownDevice = false)
protected async Task ValidateAsync(T context, ValidatedTokenRequest request,
CustomValidatorRequestContext validatorContext)
{
var isBot = (validatorContext.CaptchaResponse?.IsBot ?? false);
var twoFactorToken = request.Raw["TwoFactorToken"]?.ToString();
var twoFactorProvider = request.Raw["TwoFactorProvider"]?.ToString();
var twoFactorRemember = request.Raw["TwoFactorRemember"]?.ToString() == "1";
var twoFactorRequest = !string.IsNullOrWhiteSpace(twoFactorToken) &&
!string.IsNullOrWhiteSpace(twoFactorProvider);
var (user, valid) = await ValidateContextAsync(context);
var valid = await ValidateContextAsync(context, validatorContext);
var user = validatorContext.User;
if (!valid)
{
await UpdateFailedAuthDetailsAsync(user, false, unknownDevice);
await UpdateFailedAuthDetailsAsync(user, false, !validatorContext.KnownDevice);
}
if (!valid || isBot)
{
await BuildErrorResultAsync("Username or password is incorrect. Try again.", false, context, user);
return;
}
@ -112,13 +120,13 @@ namespace Bit.Core.IdentityServer
AfterVerifyTwoFactor(user, twoFactorProviderType, requires2FABecauseNewDevice);
if (!verified && twoFactorProviderType != TwoFactorProviderType.Remember)
if ((!verified || isBot) && twoFactorProviderType != TwoFactorProviderType.Remember)
{
await UpdateFailedAuthDetailsAsync(user, true, unknownDevice);
await UpdateFailedAuthDetailsAsync(user, true, !validatorContext.KnownDevice);
await BuildErrorResultAsync("Two-step token is invalid. Try again.", true, context, user);
return;
}
else if (!verified && twoFactorProviderType == TwoFactorProviderType.Remember)
else if ((!verified || isBot) && twoFactorProviderType == TwoFactorProviderType.Remember)
{
// Delay for brute force.
await Task.Delay(2000);
@ -153,7 +161,7 @@ namespace Bit.Core.IdentityServer
}
}
protected abstract Task<(User, bool)> ValidateContextAsync(T context);
protected abstract Task<bool> ValidateContextAsync(T context, CustomValidatorRequestContext validatorContext);
protected async Task BuildSuccessResultAsync(User user, T context, Device device, bool sendRememberToken)
{
@ -407,9 +415,7 @@ namespace Bit.Core.IdentityServer
private void BeforeVerifyTwoFactor(User user, TwoFactorProviderType type, bool requires2FABecauseNewDevice)
{
if (type == TwoFactorProviderType.Email
&&
requires2FABecauseNewDevice)
if (type == TwoFactorProviderType.Email && requires2FABecauseNewDevice)
{
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
@ -424,9 +430,7 @@ namespace Bit.Core.IdentityServer
private void AfterVerifyTwoFactor(User user, TwoFactorProviderType type, bool requires2FABecauseNewDevice)
{
if (type == TwoFactorProviderType.Email
&&
requires2FABecauseNewDevice)
if (type == TwoFactorProviderType.Email && requires2FABecauseNewDevice)
{
user.ClearTwoFactorProviders();
}
@ -595,7 +599,7 @@ namespace Bit.Core.IdentityServer
user.LastFailedLoginDate = user.RevisionDate = utcNow;
await _userRepository.ReplaceAsync(user);
if (_captchaValidationService.ValidateFailedAuthEmailConditions(unknownDevice, user.FailedLoginCount))
if (ValidateFailedAuthEmailConditions(unknownDevice, user))
{
if (twoFactorInvalid)
{
@ -607,5 +611,12 @@ namespace Bit.Core.IdentityServer
}
}
}
private bool ValidateFailedAuthEmailConditions(bool unknownDevice, User user)
{
var failedLoginCeiling = _globalSettings.Captcha.MaximumFailedLoginAttempts;
var failedLoginCount = user?.FailedLoginCount ?? 0;
return unknownDevice && failedLoginCeiling > 0 && failedLoginCount == failedLoginCeiling;
}
}
}