Fix Unrestricted Resource Consumption in Tornado
Tornado's non-blocking architecture is highly efficient, but it becomes a liability when resource allocation is unconstrained. Unrestricted Resource Consumption (CWE-400) in Tornado typically manifests as memory exhaustion via massive POST bodies or CPU/socket pinning through slow-client attacks. If you don't explicitly limit what a client can push, an attacker will eventually OOM (Out Of Memory) your service or saturate the event loop, effectively killing the application for all users.
The Vulnerable Pattern
import tornado.ioloop import tornado.webclass UnsafeHandler(tornado.web.RequestHandler): async def post(self): # VULNERABILITY: The entire request body is buffered into memory. # If the server has no max_body_size limit, an attacker can send # gigabytes of data to trigger an Out of Memory (OOM) crash. data = self.request.body self.write(f”Processed {len(data)} bytes”)
def make_app(): # VULNERABILITY: Default Application settings do not enforce # strict limits on incoming request sizes. return tornado.web.Application([ (r”/upload”, UnsafeHandler), ])
if name == “main”: app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
The Secure Implementation
To prevent resource exhaustion, you must implement defense-in-depth at two levels. First, the 'HTTPServer' must be initialized with 'max_body_size' and 'max_buffer_size' to drop oversized requests before they hit your application logic. Second, for handlers expecting large data, use the '@tornado.web.stream_request_body' decorator. This forces the server to use 'data_received' to process chunks as they arrive, preventing the entire payload from being buffered into RAM. Finally, always implement timeouts on asynchronous operations to prevent 'Slowloris' style attacks from pinning the event loop indefinitely.
import tornado.ioloop import tornado.web import tornado.httpserver@tornado.web.stream_request_body class SecureHandler(tornado.web.RequestHandler): def prepare(self): # Limit memory footprint by processing chunks self.bytes_received = 0
def data_received(self, chunk): self.bytes_received += len(chunk) # Logic to write chunk to disk or process stream async def post(self): self.write(f"Safely processed {self.bytes_received} bytes")def make_app(): return tornado.web.Application([ (r”/upload”, SecureHandler), ])
if name == “main”: app = make_app() # FIX: Enforce max_body_size (e.g., 10MB) and max_buffer_size at the Server level server = tornado.httpserver.HTTPServer( app, max_body_size=10485760, max_buffer_size=10485760 ) server.listen(8888) tornado.ioloop.IOLoop.current().start()
Your Tornado API
might be exposed to Unrestricted Resource Consumption
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.