Files
QuantEngineByItz/src/dotnet/QuantEngine.Web/Services/IIpLockoutService.cs
T
kjh2064 a9986fbc4c
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 22s
Prepare Release / Build & Create Release (push) Successful in 59s
Prepare Release / Release Notification (push) Successful in 1s
Validators (Pushes and Pull Requests) / validate-core (push) Successful in 2m1s
feat(web): implement progressive IP lockout duration scaling based on block counts
2026-07-12 13:26:30 +09:00

72 lines
2.2 KiB
C#

namespace QuantEngine.Web.Services;
public interface IIpLockoutService
{
bool IsLockedOut(string ipAddress);
void RecordFailedAttempt(string ipAddress);
void ClearFailedAttempts(string ipAddress);
}
public class IpLockoutService : IIpLockoutService
{
private readonly Dictionary<string, (int Attempts, int LockoutCount, DateTime LockedUntil)> _attemptLog = [];
private const int MaxFailedAttempts = 3;
private const int LockoutDurationMinutes = 15;
private const int MaxLockoutDurationMinutes = 1440; // 24 hours cap
private readonly object _lock = new();
public bool IsLockedOut(string ipAddress)
{
lock (_lock)
{
if (_attemptLog.TryGetValue(ipAddress, out var record))
{
if (DateTime.UtcNow < record.LockedUntil)
return true;
// Reset failed attempts but keep the LockoutCount to calculate progressive delay on next failure
if (record.Attempts >= MaxFailedAttempts)
{
_attemptLog[ipAddress] = (0, record.LockoutCount, DateTime.MinValue);
}
}
return false;
}
}
public void RecordFailedAttempt(string ipAddress)
{
lock (_lock)
{
if (_attemptLog.TryGetValue(ipAddress, out var record))
{
record.Attempts++;
if (record.Attempts >= MaxFailedAttempts)
{
// Calculate exponential progressive lockout duration: 15 * 2^LockoutCount
var durationMinutes = LockoutDurationMinutes * Math.Pow(2, record.LockoutCount);
durationMinutes = Math.Min(durationMinutes, MaxLockoutDurationMinutes);
record.LockedUntil = DateTime.UtcNow.AddMinutes(durationMinutes);
record.LockoutCount++;
}
_attemptLog[ipAddress] = record;
}
else
{
_attemptLog[ipAddress] = (1, 0, DateTime.MinValue);
}
}
}
public void ClearFailedAttempts(string ipAddress)
{
lock (_lock)
{
_attemptLog.Remove(ipAddress);
}
}
}