mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-06 17:10:13 +00:00
Optimize ApiAuthenticationMiddleware for bruteforcing
We can favour bruteforcers by checking first if the client is even eligible for talking with us, this will (in a very negligible way) improve defense against common DoS. Also rewrite Timer initialization while at it. This is internal class and we don't expect this middleware to be initialized more than once anyway.
This commit is contained in:
@@ -47,8 +47,7 @@ namespace ArchiSteamFarm.IPC.Integration {
|
|||||||
|
|
||||||
private static readonly SemaphoreSlim AuthorizationSemaphore = new(1, 1);
|
private static readonly SemaphoreSlim AuthorizationSemaphore = new(1, 1);
|
||||||
private static readonly ConcurrentDictionary<IPAddress, byte> FailedAuthorizations = new();
|
private static readonly ConcurrentDictionary<IPAddress, byte> FailedAuthorizations = new();
|
||||||
|
private static readonly Timer ClearFailedAuthorizationsTimer = new(ClearFailedAuthorizations);
|
||||||
private static Timer? ClearFailedAuthorizationsTimer;
|
|
||||||
|
|
||||||
private readonly ForwardedHeadersOptions ForwardedHeadersOptions;
|
private readonly ForwardedHeadersOptions ForwardedHeadersOptions;
|
||||||
private readonly RequestDelegate Next;
|
private readonly RequestDelegate Next;
|
||||||
@@ -63,12 +62,7 @@ namespace ArchiSteamFarm.IPC.Integration {
|
|||||||
ForwardedHeadersOptions = forwardedHeadersOptions.Value ?? throw new InvalidOperationException(nameof(forwardedHeadersOptions));
|
ForwardedHeadersOptions = forwardedHeadersOptions.Value ?? throw new InvalidOperationException(nameof(forwardedHeadersOptions));
|
||||||
|
|
||||||
lock (FailedAuthorizations) {
|
lock (FailedAuthorizations) {
|
||||||
ClearFailedAuthorizationsTimer ??= new Timer(
|
ClearFailedAuthorizationsTimer.Change(TimeSpan.FromHours(FailedAuthorizationsCooldownInHours), TimeSpan.FromHours(FailedAuthorizationsCooldownInHours));
|
||||||
_ => FailedAuthorizations.Clear(),
|
|
||||||
null,
|
|
||||||
TimeSpan.FromHours(FailedAuthorizationsCooldownInHours), // Delay
|
|
||||||
TimeSpan.FromHours(FailedAuthorizationsCooldownInHours) // Period
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,21 +95,23 @@ namespace ArchiSteamFarm.IPC.Integration {
|
|||||||
await context.Response.WriteJsonAsync(new GenericResponse<StatusCodeResponse>(false, statusCodeResponse), jsonOptions.Value.SerializerSettings).ConfigureAwait(false);
|
await context.Response.WriteJsonAsync(new GenericResponse<StatusCodeResponse>(false, statusCodeResponse), jsonOptions.Value.SerializerSettings).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ClearFailedAuthorizations(object? state = null) => FailedAuthorizations.Clear();
|
||||||
|
|
||||||
private async Task<(HttpStatusCode StatusCode, bool Permanent)> GetAuthenticationStatus(HttpContext context) {
|
private async Task<(HttpStatusCode StatusCode, bool Permanent)> GetAuthenticationStatus(HttpContext context) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ClearFailedAuthorizationsTimer == null) {
|
|
||||||
throw new InvalidOperationException(nameof(ClearFailedAuthorizationsTimer));
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress? clientIP = context.Connection.RemoteIpAddress;
|
IPAddress? clientIP = context.Connection.RemoteIpAddress;
|
||||||
|
|
||||||
if (clientIP == null) {
|
if (clientIP == null) {
|
||||||
throw new InvalidOperationException(nameof(clientIP));
|
throw new InvalidOperationException(nameof(clientIP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FailedAuthorizations.TryGetValue(clientIP, out byte attempts) && (attempts >= MaxFailedAuthorizationAttempts)) {
|
||||||
|
return (HttpStatusCode.Forbidden, false);
|
||||||
|
}
|
||||||
|
|
||||||
string? ipcPassword = ASF.GlobalConfig != null ? ASF.GlobalConfig.IPCPassword : GlobalConfig.DefaultIPCPassword;
|
string? ipcPassword = ASF.GlobalConfig != null ? ASF.GlobalConfig.IPCPassword : GlobalConfig.DefaultIPCPassword;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(ipcPassword)) {
|
if (string.IsNullOrEmpty(ipcPassword)) {
|
||||||
@@ -138,12 +134,6 @@ namespace ArchiSteamFarm.IPC.Integration {
|
|||||||
return (ForwardedHeadersOptions.KnownNetworks.Any(network => network.Contains(clientIP)) ? HttpStatusCode.OK : HttpStatusCode.Forbidden, true);
|
return (ForwardedHeadersOptions.KnownNetworks.Any(network => network.Contains(clientIP)) ? HttpStatusCode.OK : HttpStatusCode.Forbidden, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FailedAuthorizations.TryGetValue(clientIP, out byte attempts)) {
|
|
||||||
if (attempts >= MaxFailedAuthorizationAttempts) {
|
|
||||||
return (HttpStatusCode.Forbidden, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.Request.Headers.TryGetValue(HeadersField, out StringValues passwords) && !context.Request.Query.TryGetValue("password", out passwords)) {
|
if (!context.Request.Headers.TryGetValue(HeadersField, out StringValues passwords) && !context.Request.Query.TryGetValue("password", out passwords)) {
|
||||||
return (HttpStatusCode.Unauthorized, true);
|
return (HttpStatusCode.Unauthorized, true);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user