fix: add memory protection to lookup rate limiting
ENUM-002: Lookup rate limit now respects LOOKUP_RATE_LIMIT_MAX_ENTRIES (default 10000) to prevent memory exhaustion from unique IP flood. Eviction strategy: expired entries first, then oldest by last request.
This commit is contained in:
@@ -339,11 +339,33 @@ def check_lookup_rate_limit(client_ip: str) -> tuple[bool, int]:
|
|||||||
|
|
||||||
window = current_app.config.get("LOOKUP_RATE_LIMIT_WINDOW", 60)
|
window = current_app.config.get("LOOKUP_RATE_LIMIT_WINDOW", 60)
|
||||||
max_requests = current_app.config.get("LOOKUP_RATE_LIMIT_MAX", 60)
|
max_requests = current_app.config.get("LOOKUP_RATE_LIMIT_MAX", 60)
|
||||||
|
max_entries = current_app.config.get("LOOKUP_RATE_LIMIT_MAX_ENTRIES", 10000)
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
cutoff = now - window
|
cutoff = now - window
|
||||||
|
|
||||||
with _lookup_rate_limit_lock:
|
with _lookup_rate_limit_lock:
|
||||||
|
# ENUM-002: Memory protection - prune if at capacity
|
||||||
|
if len(_lookup_rate_limit_requests) >= max_entries:
|
||||||
|
if client_ip not in _lookup_rate_limit_requests:
|
||||||
|
# Evict expired entries first
|
||||||
|
expired = [
|
||||||
|
ip
|
||||||
|
for ip, reqs in _lookup_rate_limit_requests.items()
|
||||||
|
if not reqs or reqs[-1] <= cutoff
|
||||||
|
]
|
||||||
|
for ip in expired:
|
||||||
|
del _lookup_rate_limit_requests[ip]
|
||||||
|
|
||||||
|
# If still at capacity, evict oldest entries
|
||||||
|
if len(_lookup_rate_limit_requests) >= max_entries:
|
||||||
|
sorted_ips = sorted(
|
||||||
|
_lookup_rate_limit_requests.items(),
|
||||||
|
key=lambda x: x[1][-1] if x[1] else 0,
|
||||||
|
)
|
||||||
|
for ip, _ in sorted_ips[: max_entries // 4]:
|
||||||
|
del _lookup_rate_limit_requests[ip]
|
||||||
|
|
||||||
requests = _lookup_rate_limit_requests[client_ip]
|
requests = _lookup_rate_limit_requests[client_ip]
|
||||||
requests[:] = [t for t in requests if t > cutoff]
|
requests[:] = [t for t in requests if t > cutoff]
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ class Config:
|
|||||||
)
|
)
|
||||||
LOOKUP_RATE_LIMIT_WINDOW = int(os.environ.get("FLASKPASTE_LOOKUP_RATE_WINDOW", "60"))
|
LOOKUP_RATE_LIMIT_WINDOW = int(os.environ.get("FLASKPASTE_LOOKUP_RATE_WINDOW", "60"))
|
||||||
LOOKUP_RATE_LIMIT_MAX = int(os.environ.get("FLASKPASTE_LOOKUP_RATE_MAX", "60"))
|
LOOKUP_RATE_LIMIT_MAX = int(os.environ.get("FLASKPASTE_LOOKUP_RATE_MAX", "60"))
|
||||||
|
# ENUM-002: Maximum tracked IPs for lookup rate limiting (memory protection)
|
||||||
|
LOOKUP_RATE_LIMIT_MAX_ENTRIES = int(
|
||||||
|
os.environ.get("FLASKPASTE_LOOKUP_RATE_MAX_ENTRIES", "10000")
|
||||||
|
)
|
||||||
|
|
||||||
# Audit Logging
|
# Audit Logging
|
||||||
# Track security-relevant events (paste creation, deletion, rate limits, etc.)
|
# Track security-relevant events (paste creation, deletion, rate limits, etc.)
|
||||||
|
|||||||
Reference in New Issue
Block a user