optimization
This commit is contained in:
parent
fe2dd3c394
commit
75aebdd9b2
@ -355,16 +355,6 @@ public class AuthenticationController : ControllerBase
|
||||
inDto.PasswordHash.ThrowIfNull(new ArgumentException(@"PasswordHash empty", "PasswordHash"));
|
||||
}
|
||||
|
||||
var secretEmail = SetupInfo.IsSecretEmail(inDto.UserName);
|
||||
|
||||
var requestIp = MessageSettings.GetIP(Request);
|
||||
var bruteForceSuccessAttempt = _bruteForceLoginManager.Increment(inDto.UserName, requestIp, out _);
|
||||
|
||||
if (!secretEmail && !bruteForceSuccessAttempt)
|
||||
{
|
||||
throw new BruteForceCredentialException();
|
||||
}
|
||||
|
||||
inDto.PasswordHash = (inDto.PasswordHash ?? "").Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(inDto.PasswordHash))
|
||||
@ -375,22 +365,11 @@ public class AuthenticationController : ControllerBase
|
||||
{
|
||||
inDto.PasswordHash = _passwordHasher.GetClientPassword(inDto.Password);
|
||||
}
|
||||
}
|
||||
|
||||
user = _userManager.GetUsersByPasswordHash(
|
||||
_tenantManager.GetCurrentTenant().Id,
|
||||
inDto.UserName,
|
||||
inDto.PasswordHash);
|
||||
|
||||
if (user == null || !_userManager.UserExists(user))
|
||||
{
|
||||
throw new Exception("user not found");
|
||||
}
|
||||
|
||||
if (!secretEmail)
|
||||
{
|
||||
_bruteForceLoginManager.Decrement(inDto.UserName, requestIp);
|
||||
}
|
||||
var requestIp = MessageSettings.GetIP(Request);
|
||||
|
||||
user = _bruteForceLoginManager.Attempt(inDto.UserName, inDto.PasswordHash, requestIp, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -24,82 +24,93 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
namespace ASC.Web.Core;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
namespace ASC.Web.Core;
|
||||
[Scope]
|
||||
public class BruteForceLoginManager
|
||||
{
|
||||
private readonly SettingsManager _settingsManager;
|
||||
private readonly SettingsManager _settingsManager;
|
||||
private readonly UserManager _userManager;
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
public BruteForceLoginManager(SettingsManager settingsManager, IDistributedCache distributedCache)
|
||||
public BruteForceLoginManager(SettingsManager settingsManager, UserManager userManager, TenantManager tenantManager, IDistributedCache distributedCache)
|
||||
{
|
||||
_settingsManager = settingsManager;
|
||||
_settingsManager = settingsManager;
|
||||
_userManager = userManager;
|
||||
_tenantManager = tenantManager;
|
||||
_distributedCache = distributedCache;
|
||||
}
|
||||
|
||||
public bool Increment(string login, string requestIp, out bool showRecaptcha)
|
||||
{
|
||||
showRecaptcha = true;
|
||||
public UserInfo Attempt(string login, string passwordHash, string requestIp, out bool showRecaptcha)
|
||||
{
|
||||
UserInfo user = null;
|
||||
|
||||
showRecaptcha = true;
|
||||
|
||||
var blockCacheKey = GetBlockCacheKey(login, requestIp);
|
||||
|
||||
if (GetFromCache<string>(blockCacheKey) != null)
|
||||
{
|
||||
throw new BruteForceCredentialException();
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var blockCacheKey = GetBlockCacheKey(login, requestIp);
|
||||
|
||||
if (GetFromCache<string>(blockCacheKey) != null)
|
||||
{
|
||||
return false;
|
||||
throw new BruteForceCredentialException();
|
||||
}
|
||||
|
||||
string historyCacheKey = null;
|
||||
var now = DateTime.UtcNow;
|
||||
LoginSettingsWrapper settings = null;
|
||||
List<DateTime> history = null;
|
||||
var secretEmail = SetupInfo.IsSecretEmail(login);
|
||||
|
||||
if (!secretEmail)
|
||||
{
|
||||
historyCacheKey = GetHistoryCacheKey(login, requestIp);
|
||||
|
||||
settings = new LoginSettingsWrapper(_settingsManager.Load<LoginSettings>());
|
||||
var checkTime = now.Subtract(settings.CheckPeriod);
|
||||
|
||||
history = GetFromCache<List<DateTime>>(historyCacheKey) ?? new List<DateTime>();
|
||||
history = history.Where(item => item > checkTime).ToList();
|
||||
history.Add(now);
|
||||
|
||||
showRecaptcha = history.Count > settings.AttemptCount - 1;
|
||||
|
||||
if (history.Count > settings.AttemptCount)
|
||||
{
|
||||
SetToCache(blockCacheKey, "block", now.Add(settings.BlockTime));
|
||||
_distributedCache.Remove(historyCacheKey);
|
||||
throw new BruteForceCredentialException();
|
||||
}
|
||||
|
||||
SetToCache(historyCacheKey, history, now.Add(settings.CheckPeriod));
|
||||
}
|
||||
|
||||
user = _userManager.GetUsersByPasswordHash(
|
||||
_tenantManager.GetCurrentTenant().Id,
|
||||
login,
|
||||
passwordHash);
|
||||
|
||||
if (user == null || !_userManager.UserExists(user))
|
||||
{
|
||||
throw new Exception("user not found");
|
||||
}
|
||||
|
||||
if (!secretEmail)
|
||||
{
|
||||
history.RemoveAt(history.Count - 1);
|
||||
|
||||
SetToCache(historyCacheKey, history, now.Add(settings.CheckPeriod));
|
||||
}
|
||||
|
||||
var historyCacheKey = GetHistoryCacheKey(login, requestIp);
|
||||
|
||||
var history = GetFromCache<List<DateTime>>(historyCacheKey) ?? new List<DateTime>();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var settings = _settingsManager.Load<LoginSettings>();
|
||||
|
||||
var checkTime = now.Subtract(TimeSpan.FromSeconds(settings.CheckPeriod));
|
||||
|
||||
history = history.Where(item => item > checkTime).ToList();
|
||||
|
||||
history.Add(now);
|
||||
|
||||
showRecaptcha = history.Count > settings.AttemptCount - 1;
|
||||
|
||||
if (history.Count > settings.AttemptCount)
|
||||
{
|
||||
SetToCache(blockCacheKey, "block", now.Add(TimeSpan.FromSeconds(settings.BlockTime)));
|
||||
_distributedCache.Remove(historyCacheKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetToCache(historyCacheKey, history, now.Add(TimeSpan.FromSeconds(settings.CheckPeriod)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Decrement(string login, string requestIp)
|
||||
{
|
||||
var historyCacheKey = GetHistoryCacheKey(login, requestIp);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var history = GetFromCache<List<DateTime>>(historyCacheKey) ?? new List<DateTime>();
|
||||
|
||||
if (history.Count > 0)
|
||||
{
|
||||
history.RemoveAt(history.Count - 1);
|
||||
}
|
||||
|
||||
var settings = _settingsManager.Load<LoginSettings>();
|
||||
|
||||
SetToCache(historyCacheKey, history, DateTime.UtcNow.Add(TimeSpan.FromSeconds(settings.CheckPeriod)));
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private T GetFromCache<T>(string key)
|
||||
@ -108,7 +119,7 @@ public class BruteForceLoginManager
|
||||
|
||||
if (serializedObject == null)
|
||||
{
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream(serializedObject);
|
||||
@ -128,13 +139,7 @@ public class BruteForceLoginManager
|
||||
});
|
||||
}
|
||||
|
||||
private string GetBlockCacheKey(string login, string requestIp)
|
||||
{
|
||||
return "loginblock/" + login + requestIp;
|
||||
}
|
||||
private static string GetBlockCacheKey(string login, string requestIp) => $"loginblock/{login}/{requestIp}";
|
||||
|
||||
private string GetHistoryCacheKey(string login, string requestIp)
|
||||
{
|
||||
return "loginsec/" + login + requestIp;
|
||||
}
|
||||
}
|
||||
private static string GetHistoryCacheKey(string login, string requestIp) => $"loginsec/{login}/{requestIp}";
|
||||
}
|
||||
|
@ -116,12 +116,11 @@ global using Google.Authenticator;
|
||||
global using Microsoft.AspNetCore.Builder;
|
||||
global using Microsoft.AspNetCore.Hosting;
|
||||
global using Microsoft.AspNetCore.Http;
|
||||
global using Microsoft.AspNetCore.Mvc;
|
||||
global using Microsoft.AspNetCore.WebUtilities;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using Microsoft.Extensions.Caching.Distributed;
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
|
||||
global using MimeKit.Utils;
|
||||
@ -129,6 +128,8 @@ global using MimeKit.Utils;
|
||||
global using Newtonsoft.Json;
|
||||
global using Newtonsoft.Json.Linq;
|
||||
|
||||
global using ProtoBuf;
|
||||
|
||||
global using SixLabors.ImageSharp;
|
||||
global using SixLabors.ImageSharp.Drawing.Processing;
|
||||
global using SixLabors.ImageSharp.Formats;
|
||||
@ -143,4 +144,4 @@ global using Twilio.Rest.Api.V2010.Account;
|
||||
global using Twilio.Types;
|
||||
|
||||
global using SecurityContext = ASC.Core.SecurityContext;
|
||||
global using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute;
|
||||
global using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute;
|
||||
|
@ -24,7 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Web.Core.Utility.Settings;
|
||||
namespace ASC.Web.Core.Utility.Settings;
|
||||
|
||||
public class LoginSettings : ISettings<LoginSettings>
|
||||
{
|
||||
public int AttemptCount { get; set; }
|
||||
@ -44,4 +45,20 @@ public class LoginSettings : ISettings<LoginSettings>
|
||||
CheckPeriod = 60
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class LoginSettingsWrapper
|
||||
{
|
||||
private readonly LoginSettings _loginSettings;
|
||||
|
||||
public int AttemptCount { get => _loginSettings.AttemptCount; }
|
||||
|
||||
public TimeSpan BlockTime { get => TimeSpan.FromSeconds(_loginSettings.BlockTime); }
|
||||
|
||||
public TimeSpan CheckPeriod { get => TimeSpan.FromSeconds(_loginSettings.CheckPeriod); }
|
||||
|
||||
public LoginSettingsWrapper(LoginSettings loginSettings)
|
||||
{
|
||||
_loginSettings = loginSettings;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user