Skip to content

Commit

Permalink
Auth rate-limit
Browse files Browse the repository at this point in the history
  • Loading branch information
veniware committed Oct 4, 2024
1 parent bb4f83f commit f6947ab
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 17 deletions.
12 changes: 5 additions & 7 deletions Protest/Http/Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,17 @@ public static bool AttemptAuthentication(HttpListenerContext ctx, out string ses

//TODO: check all users access

bool successful = access.isDomainUser && OperatingSystem.IsWindows() ?
Protocols.Kerberos.TryDirectoryAuthentication(username, password) :
Cryptography.HashUsernameAndPassword(username, password).SequenceEqual(access.hash);

IPAddress remoteIp = ctx.Request.RemoteEndPoint.Address;
bool successful = access.isDomainUser && OperatingSystem.IsWindows()
? Protocols.Kerberos.TryDirectoryAuthentication(username, password)
: Cryptography.HashUsernameAndPassword(username, password).SequenceEqual(access.hash);

if (successful) {
Logger.Action(username, $"Successfully logged in from {remoteIp}");
Logger.Action(username, $"Successfully logged in from {ctx.Request.RemoteEndPoint.Address}");
sessionId = GrandAccess(ctx, username);
return true;
}

Logger.Action(username, $"Unsuccessful login attempt from {remoteIp}");
Logger.Action(username, $"Unsuccessful login attempt from {ctx.Request.RemoteEndPoint.Address}");
sessionId = null;
return false;
}
Expand Down
59 changes: 49 additions & 10 deletions Protest/Http/Listener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#endif

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Net;
Expand All @@ -18,6 +19,10 @@ public sealed class Listener {
private readonly HttpListener listener;
private readonly Cache cache;

private const long RATE_LIMIT_TIME_WINDOW = 6_000_000_000; //10 minutes
private const int MAX_REQUESTS_PER_WIN_PERIOD = 5;
private static readonly ConcurrentDictionary<IPAddress, List<long>> rateLimLog = new ConcurrentDictionary<IPAddress, List<long>>();

private static readonly Dictionary<string, Func<HttpListenerContext, Dictionary<string, string>, string, byte[]>> routing
= new Dictionary<string, Func<HttpListenerContext, Dictionary<string, string>, string, byte[]>> {
{ "/logout", (ctx, parameters, username) => Auth.RevokeAccess(ctx, username) ? Data.CODE_OK.Array : Data.CODE_FAILED.Array },
Expand Down Expand Up @@ -263,16 +268,7 @@ private void ListenerCallback(IAsyncResult result) {
string path = ctx.Request.Url.PathAndQuery;

if (String.Equals(path, "/auth", StringComparison.Ordinal)) {
if (!String.Equals(ctx.Request.HttpMethod, "POST", StringComparison.Ordinal)) {
ctx.Response.StatusCode = (int)HttpStatusCode.BadRequest;
ctx.Response.Close();
return;
}

ctx.Response.StatusCode = Auth.AttemptAuthentication(ctx, out _)
? (int)HttpStatusCode.Accepted
: (int)HttpStatusCode.Unauthorized;

AuthHandler(ctx);
ctx.Response.Close();
return;
}
Expand Down Expand Up @@ -429,6 +425,49 @@ private bool CacheHandler(HttpListenerContext ctx, string path) {
return true;
}

private static bool AuthHandler(HttpListenerContext ctx) {
if (!String.Equals(ctx.Request.HttpMethod, "POST", StringComparison.Ordinal)) {
ctx.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return false;
}

IPAddress clientIP = ctx.Request.RemoteEndPoint?.Address;
if (clientIP is null) {
ctx.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return false;
}

if (IsRateLimited(clientIP)) {
ctx.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
return false;
}

bool isSuccessful = Auth.AttemptAuthentication(ctx, out _);
ctx.Response.StatusCode = isSuccessful
? (int)HttpStatusCode.Accepted
: (int)HttpStatusCode.Unauthorized;

return true;
}

private static bool IsRateLimited(IPAddress clientIP) {
List<long> timestamps = rateLimLog.GetOrAdd(clientIP, _ => new List<long>());

long currentTime = DateTime.UtcNow.Ticks;
for (int i = timestamps.Count - 1; i >= 0; i--) {
if (currentTime - timestamps[i] > RATE_LIMIT_TIME_WINDOW) {
timestamps.RemoveAt(i);
}
}

if (rateLimLog[clientIP].Count >= MAX_REQUESTS_PER_WIN_PERIOD) {
return true;
}

timestamps.Add(currentTime);
return false;
}

private static bool DynamicHandler(HttpListenerContext ctx, Dictionary<string, string> parameters) {
string sessionId = ctx.Request.Cookies["sessionid"]?.Value ?? null;
string username = IPAddress.IsLoopback(ctx.Request.RemoteEndPoint.Address) ? "loopback" : Auth.GetUsername(sessionId);
Expand Down

0 comments on commit f6947ab

Please sign in to comment.