Skip to content

Add per-IP rate limiting to prevent DoS attacks#8

Merged
vladiant merged 2 commits intomainfrom
add_rate_limiter
Mar 31, 2026
Merged

Add per-IP rate limiting to prevent DoS attacks#8
vladiant merged 2 commits intomainfrom
add_rate_limiter

Conversation

@vladiant
Copy link
Copy Markdown
Collaborator

Summary

Adds in-memory sliding-window rate limiting to all upload, processing, read, and retention endpoints, mitigating denial-of-service attacks by throttling excessive requests per client IP. Returns HTTP 429 (Too Many Requests) when limits are exceeded.

Motivation

Upload and processing endpoints were previously unprotected against request flooding. A single client could exhaust server resources (CPU, DB connections, disk I/O, bandwidth) by sending unbounded requests. Read endpoints (GET) are also vulnerable to connection pool and bandwidth exhaustion.

Changes

File What changed
rate_limit.py NewRateLimiter class: thread-safe sliding-window limiter keyed by client IP (X-Forwarded-For or socket address)
config.py Added 6 rate limit settings with IMG_ env var prefix
dependencies.py Added upload_rate_limiter(), process_rate_limiter(), read_rate_limiter() DI factories
images.py Applied rate limiters to all 6 image endpoints (upload, list, get, download, process, batch process)
retention.py Applied process rate limiter to retention sweep
test_rate_limit.py New — 5 tests covering allow/block, window expiry, per-IP isolation, X-Forwarded-For parsing
README.md Documented rate limiting and new env vars
PROJECT_DESCRIPTION.md Added rate limiting to API quality features, status codes, and config table
pyproject.toml Version bump 1.2.31.2.4
CHANGELOG.md Added [1.2.4] Security entry

Default rate limits

Endpoint group Max requests Window Env vars
Upload (POST /images/) 10 60s IMG_RATE_LIMIT_UPLOAD_MAX, IMG_RATE_LIMIT_UPLOAD_WINDOW
Process (POST /*/process, /retention/sweep) 20 60s IMG_RATE_LIMIT_PROCESS_MAX, IMG_RATE_LIMIT_PROCESS_WINDOW
Read (GET /images/*) 60 60s IMG_RATE_LIMIT_READ_MAX, IMG_RATE_LIMIT_READ_WINDOW

Design decisions

  • In-memory over external store — No Redis/external dependency needed; sufficient for single-instance and demo use. For multi-replica production deployments, swap to a distributed backend.
  • Sliding window — More accurate than fixed-window counters; prevents burst-at-boundary attacks.
  • Per-endpoint-group granularity — Upload (most expensive) gets the strictest limit; reads get the most lenient.
  • X-Forwarded-For support — Uses the first IP in the chain for correctness behind reverse proxies.
  • Thread-safe — Uses threading.Lock since FastAPI may run dependencies in thread pool.

Verification

  • ruff: all checks passed
  • mypy: no issues in 46 source files
  • pytest: 87 passed (81% coverage)
  • SemVer: PATCH bump (backward-compatible security addition)

@vladiant vladiant merged commit 9c204f4 into main Mar 31, 2026
4 checks passed
@vladiant vladiant deleted the add_rate_limiter branch March 31, 2026 05:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant