add nonce rate limit
This commit is contained in:
parent
4e08d1a2df
commit
7b9e499ff3
2 changed files with 9 additions and 11 deletions
|
|
@ -456,8 +456,9 @@ public class Requests
|
||||||
|
|
||||||
string id = Utils.GetValidIdOrZero(idQuery.ToString());
|
string id = Utils.GetValidIdOrZero(idQuery.ToString());
|
||||||
|
|
||||||
//rate limit pobran nonce (konto)
|
//rate limit pobran nonce (IP+konto): jeden atakujący nie zjada budżetu konta innym IP
|
||||||
if (RateLimited(NonceRateLimits, id, NONCE_RATE_LIMIT))
|
string clientIp = context.Connection.RemoteIpAddress?.ToString() ?? "";
|
||||||
|
if (RateLimited(NonceRateLimits, $"{clientIp}_{id}", NONCE_RATE_LIMIT))
|
||||||
{
|
{
|
||||||
string pass = await Utils.GetPassword(id);
|
string pass = await Utils.GetPassword(id);
|
||||||
await context.Response.WriteAsync(Encryption.Encryption.EncryptForClient("error:rate.limit", pass));
|
await context.Response.WriteAsync(Encryption.Encryption.EncryptForClient("error:rate.limit", pass));
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public class Utils
|
||||||
public static ConcurrentDictionary<string, (int count, long resetAt)> AccountInitRateLimits = new();
|
public static ConcurrentDictionary<string, (int count, long resetAt)> AccountInitRateLimits = new();
|
||||||
|
|
||||||
public static int AUTH_RATE_LIMIT = 10; // failed password verifications per account+IP per minute
|
public static int AUTH_RATE_LIMIT = 10; // failed password verifications per account+IP per minute
|
||||||
public static int NONCE_RATE_LIMIT = 240; // pobrania nonce per konto per minute
|
public static int NONCE_RATE_LIMIT = 120; // pobrania nonce per IP+konto per minute
|
||||||
public static int KDFSALT_RATE_LIMIT = 60; // pobrania soli per IP per minute
|
public static int KDFSALT_RATE_LIMIT = 60; // pobrania soli per IP per minute
|
||||||
public static int ACCOUNT_INIT_RATE_LIMIT = 4; // utworzone konta per IP per hour (check w init, zapis w finish)
|
public static int ACCOUNT_INIT_RATE_LIMIT = 4; // utworzone konta per IP per hour (check w init, zapis w finish)
|
||||||
|
|
||||||
|
|
@ -45,11 +45,7 @@ public class Utils
|
||||||
public static bool RateLimited(ConcurrentDictionary<string, (int count, long resetAt)> store, string key, int limit, int windowSeconds = 60)
|
public static bool RateLimited(ConcurrentDictionary<string, (int count, long resetAt)> store, string key, int limit, int windowSeconds = 60)
|
||||||
{
|
{
|
||||||
if (limit <= 0) return false; // <=0 wyłącza limit
|
if (limit <= 0) return false; // <=0 wyłącza limit
|
||||||
long nowSec = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
return RecordRateLimitHit(store, key, windowSeconds) > limit;
|
||||||
var window = store.AddOrUpdate(key,
|
|
||||||
(1, nowSec + windowSeconds),
|
|
||||||
(_, old) => old.resetAt <= nowSec ? (1, nowSec + windowSeconds) : (old.count + 1, old.resetAt));
|
|
||||||
return window.count > limit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Sprawdzenie bez inkrementacji: true jeśli klucz osiągnął już limit w bieżącym oknie
|
//Sprawdzenie bez inkrementacji: true jeśli klucz osiągnął już limit w bieżącym oknie
|
||||||
|
|
@ -67,13 +63,14 @@ public class Utils
|
||||||
return entry.count >= limit;
|
return entry.count >= limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Zapis jednego zdarzenia; okno startuje przy pierwszym zapisie
|
//Zapis jednego zdarzenia; okno startuje przy pierwszym zapisie. Zwraca licznik okna
|
||||||
public static void RecordRateLimitHit(ConcurrentDictionary<string, (int count, long resetAt)> store, string key, int windowSeconds = 60)
|
public static int RecordRateLimitHit(ConcurrentDictionary<string, (int count, long resetAt)> store, string key, int windowSeconds = 60)
|
||||||
{
|
{
|
||||||
long nowSec = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
long nowSec = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
store.AddOrUpdate(key,
|
var window = store.AddOrUpdate(key,
|
||||||
(1, nowSec + windowSeconds),
|
(1, nowSec + windowSeconds),
|
||||||
(_, old) => old.resetAt <= nowSec ? (1, nowSec + windowSeconds) : (old.count + 1, old.resetAt));
|
(_, old) => old.resetAt <= nowSec ? (1, nowSec + windowSeconds) : (old.count + 1, old.resetAt));
|
||||||
|
return window.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failed password verifications only, keyed "{id}:{ip}". A success clears the
|
// Failed password verifications only, keyed "{id}:{ip}". A success clears the
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue