add rate limits
All checks were successful
Server Build / publish (push) Successful in 29s
Voice Build / publish (push) Successful in 25s

This commit is contained in:
olcxja 2026-07-04 17:03:56 +02:00
commit c523d40342
4 changed files with 71 additions and 6 deletions

View file

@ -14,6 +14,27 @@ public class Requests
{ {
public static ConcurrentDictionary<string, CreateHolder> createHolder = new(); public static ConcurrentDictionary<string, CreateHolder> createHolder = new();
public static ConcurrentDictionary<string, (string, DateTimeOffset)> nonceHolder = new(); public static ConcurrentDictionary<string, (string, DateTimeOffset)> nonceHolder = new();
public static ConcurrentDictionary<string, (int count, DateTime resetTime)> actionRateLimits = new();
public static bool IsActionRateLimited(string id)
{
if (ACTION_RATE_LIMIT <= 0) return false;
var now = DateTime.UtcNow;
actionRateLimits.AddOrUpdate(id,
_ => (1, now.AddMinutes(1)),
(_, old) => {
if (now > old.resetTime) return (1, now.AddMinutes(1));
return (old.count + 1, old.resetTime);
}
);
if (actionRateLimits.TryGetValue(id, out var record) && record.count > ACTION_RATE_LIMIT)
{
return true;
}
return false;
}
public static SemaphoreSlim createLock = new SemaphoreSlim(1, 1); public static SemaphoreSlim createLock = new SemaphoreSlim(1, 1);
@ -625,6 +646,20 @@ public static async Task Auth(HttpContext context, Func<Task> next, IQueryCollec
return; return;
} }
string[] actionRequests = {
"user/dm/invite", "user/dm/invite/revoke", "user/dm/invite/decline", "user/dm/create",
"dm/message/send", "dm/message/delete", "dm/message/react", "dm/key/update",
"user/storage/public/update", "user/storage/private/update", "dm/read"
};
if (System.Array.IndexOf(actionRequests, serializedBody.string1) >= 0)
{
if (IsActionRateLimited(id))
{
await context.Response.WriteAsync(Encryption.Encryption.EncryptString("error:rate.limit", password));
return;
}
}
SemaphoreSlim userLock = Utils.GetUserLock(id); SemaphoreSlim userLock = Utils.GetUserLock(id);
await userLock.WaitAsync(); await userLock.WaitAsync();
try try
@ -733,6 +768,16 @@ public static async Task Auth(HttpContext context, Func<Task> next, IQueryCollec
await context.Response.WriteAsync(Encryption.Encryption.EncryptString(await Room.Requests.GetDmMessages(id, msgGet.string1, msgGet.string3, msgGet.string2), password)); await context.Response.WriteAsync(Encryption.Encryption.EncryptString(await Room.Requests.GetDmMessages(id, msgGet.string1, msgGet.string3, msgGet.string2), password));
break; break;
} }
case "dm/read":
{
Universal2String readGet = JsonSerializer.Deserialize(
serializedBody.string2,
AppJsonSerializerContext.Default.Universal2String
);
await Account.Utils.UpdateUserDm(id, readGet.string1, "true", "");
await context.Response.WriteAsync(Encryption.Encryption.EncryptString("success:dm.read", password));
break;
}
case "dm/key/get": case "dm/key/get":
await context.Response.WriteAsync( await context.Response.WriteAsync(
Encryption.Encryption.EncryptString( Encryption.Encryption.EncryptString(
@ -789,6 +834,12 @@ public static async Task Auth(HttpContext context, Func<Task> next, IQueryCollec
return; return;
} }
if (IsActionRateLimited(id))
{
await context.Response.WriteAsync(Encryption.Encryption.EncryptString("error:rate.limit", password));
return;
}
byte[] bodyBytes = await LarpixServer.Utils.Utils.LoadBodyBytes(context.Request.Body, LarpixServer.Utils.Utils.PUBLIC_STORAGE_LIMIT); byte[] bodyBytes = await LarpixServer.Utils.Utils.LoadBodyBytes(context.Request.Body, LarpixServer.Utils.Utils.PUBLIC_STORAGE_LIMIT);
byte[] fileBytes = await Utils.NonceDecryptBytes(id, password, bodyBytes, true); byte[] fileBytes = await Utils.NonceDecryptBytes(id, password, bodyBytes, true);

View file

@ -432,7 +432,10 @@ public class Utils
} }
fileDm.string1 = dmId; fileDm.string1 = dmId;
fileDm.string2 = timestamp; if (timestamp != "")
{
fileDm.string2 = timestamp;
}
fileDm.string3 = isRead; fileDm.string3 = isRead;
await Fs.WriteFile(dmPath, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(fileDm, AppJsonSerializerContext.Default.Universal3String))); await Fs.WriteFile(dmPath, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(fileDm, AppJsonSerializerContext.Default.Universal3String)));

View file

@ -97,7 +97,7 @@ public class Requests
return Encoding.UTF8.GetString(await Fs.ReadFile(path)); return Encoding.UTF8.GetString(await Fs.ReadFile(path));
} }
public static async Task<string> UpdateDmKey(string id, string dmId, string key) public static async Task<string> UpdateDmKey(string id, string dmId, string key) //nie powinno byc takich rzeczy jak ta, jak juz powinnismy tworzyc nowy klucz, np. 1 i pozniej w wiadomosci wpisywac Message.key = "1" i nim szyfrowac
{ {
if (!await IsMemberOfDm(id, dmId)) if (!await IsMemberOfDm(id, dmId))
{ {
@ -414,7 +414,7 @@ public class Requests
if (memberId == id) if (memberId == id)
{ {
await Account.Utils.UpdateUserDm(memberId, dmId, "true", msg.timestamp); await Account.Utils.UpdateUserDm(memberId, dmId, "true", msg.timestamp);
await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}"); await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}:read");
} }
else else
{ {
@ -430,7 +430,7 @@ public class Requests
{ {
userLock.Release(); userLock.Release();
} }
await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}"); await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}:unread");
}); });
} }
} }
@ -459,8 +459,10 @@ public class Requests
); );
if (msg.author != id) return "error:forbidden"; if (msg.author != id) return "error:forbidden";
if (msg.type == "larp.redacted") return "error:message.already.deleted";
msg.type = "larp.redacted"; msg.type = "larp.redacted";
msg.reactions = "";
msg.content = "{blah(larp.redacted)}"; msg.content = "{blah(larp.redacted)}";
msg.attachment = ""; msg.attachment = "";
msg.key = ""; msg.key = "";
@ -474,7 +476,7 @@ public class Requests
string memberId = Account.Utils.GetValidIdOrZero(memberWD); string memberId = Account.Utils.GetValidIdOrZero(memberWD);
if (isLocal) if (isLocal)
{ {
await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}"); await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}:update");
} }
} }
return "success:message.deleted"; return "success:message.deleted";
@ -501,6 +503,8 @@ public class Requests
AppJsonSerializerContext.Default.Message AppJsonSerializerContext.Default.Message
); );
if (msg.type == "larp.redacted") return "error:message.already.deleted";
msg.reactions = reactions ?? ""; msg.reactions = reactions ?? "";
await Fs.WriteFile(msgPath, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(msg, AppJsonSerializerContext.Default.Message))); await Fs.WriteFile(msgPath, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(msg, AppJsonSerializerContext.Default.Message)));
@ -512,7 +516,7 @@ public class Requests
string memberId = Account.Utils.GetValidIdOrZero(memberWD); string memberId = Account.Utils.GetValidIdOrZero(memberWD);
if (isLocal) if (isLocal)
{ {
await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}"); await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}:update");
} }
} }
return "success:message.reacted"; return "success:message.reacted";

View file

@ -31,6 +31,7 @@ public class Utils
public static int LOCK_SIZE = 1000000; public static int LOCK_SIZE = 1000000;
public static int MAX_BODY_SIZE = 20971520; //20mb //2621440; //2.5mb public static int MAX_BODY_SIZE = 20971520; //20mb //2621440; //2.5mb
public static int ACTION_RATE_LIMIT = 120;
public static int PRIVATE_STORAGE_LIMIT = 5242880; public static int PRIVATE_STORAGE_LIMIT = 5242880;
public static int PUBLIC_STORAGE_LIMIT = 10485760; public static int PUBLIC_STORAGE_LIMIT = 10485760;
@ -259,6 +260,12 @@ public class Utils
PORT = int.Parse(port); PORT = int.Parse(port);
} }
string? actionRateLimitStr = Environment.GetEnvironmentVariable("ACTION_RATE_LIMIT");
if (actionRateLimitStr != null && int.TryParse(actionRateLimitStr, out int parsedLimit))
{
ACTION_RATE_LIMIT = parsedLimit;
}
VOICE_IPS.Clear(); VOICE_IPS.Clear();
port = Environment.GetEnvironmentVariable("VOICE_IPS"); port = Environment.GetEnvironmentVariable("VOICE_IPS");