Fix Lack of Resources & Rate Limiting in Tornado
Tornado's non-blocking nature is a double-edged sword. While it handles concurrency well, a lack of explicit resource limits allows an attacker to saturate the I/O loop or exhaust memory through 'slowloris' style attacks or massive payload injections. If you aren't enforcing rate limits and body size constraints, your service is a sitting duck for DoS.
The Vulnerable Pattern
import tornado.ioloop import tornado.webclass VulnerableHandler(tornado.web.RequestHandler): async def post(self): # VULNERABILITY: No limit on request body size and no rate limiting. # An attacker can send gigabytes of data or spam this endpoint to exhaust RAM. data = self.request.body self.write({‘status’: ‘received’, ‘size’: len(data)})
def make_app(): return tornado.web.Application([ (r’/upload’, VulnerableHandler), ]) # No global resource limits configured
The Secure Implementation
The secure implementation mitigates resource exhaustion via two primary vectors. First, 'max_body_size' is explicitly set in the Application settings to prevent memory-based DoS from oversized payloads. Second, a custom 'limit_requests' decorator implements a sliding-window rate limiter on sensitive endpoints, ensuring that a single IP cannot monopolize the event loop. For production-grade security, always offload rate-limiting state to a fast K/V store like Redis and use a reverse proxy like Nginx for initial connection throttling.
import time from functools import wraps from tornado.web import HTTPError, RequestHandler, ApplicationSimple in-memory bucket (Use Redis for production/distributed environments)
ratelimit_store = {}
def limit_requests(max_reqs, window): def decorator(method): @wraps(method) async def wrapper(self, *args, **kwargs): ip = self.request.remote_ip now = time.time() calls = [t for t in ratelimit_store.get(ip, []) if t > now - window] if len(calls) >= max_reqs: raise HTTPError(429, ‘Rate limit exceeded’) ratelimit_store[ip] = calls + [now] return await method(self, *args, **kwargs) return wrapper return decorator
class SecureHandler(RequestHandler): @limit_requests(max_reqs=5, window=60) async def post(self): self.write({‘status’: ‘secure’})
def make_app(): return Application([ (r’/upload’, SecureHandler), ], max_body_size=1024 * 1024 * 2, # SECURE: Limit body size to 2MB max_buffer_size=1024 * 1024 * 4 # SECURE: Limit internal buffer size )
Your Tornado API
might be exposed to Lack of Resources & Rate Limiting
74% of Tornado apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.