Fix Improper Error Handling in Tornado
Tornado's default error handling is a reconnaissance gift for attackers. By default, if an unhandled exception occurs or if 'debug=True' is set, the framework spills the entire stack trace, environment details, and library versions directly to the HTTP response. To harden a Tornado application, you must intercept the error lifecycle by overriding the 'write_error' method in your RequestHandler to ensure internal state never crosses the network boundary.
The Vulnerable Pattern
import tornado.ioloop import tornado.webclass VulnerableHandler(tornado.web.RequestHandler): def get(self): # Logic error that triggers a traceback db_connection = None db_connection.execute(“SELECT * FROM users”)
def make_app(): # debug=True is often left on in production, leaking traces return tornado.web.Application([ (r”/vulnerable”, VulnerableHandler), ], debug=True)
The Secure Implementation
The vulnerability stems from the default implementation of 'write_error' in 'tornado.web.RequestHandler', which renders an HTML page containing the traceback if debugging is enabled or if not overridden. The fix involves three steps: 1. Create a 'BaseHandler' that overrides 'write_error'. 2. Use 'kwargs.get("exc_info")' to log the actual error details to a secure internal log file rather than the HTTP response. 3. Return a generic, sanitized JSON message to the client. Finally, ensure 'debug=False' is explicitly set in your production Application configuration to disable Tornado's built-in development tools.
import tornado.web import logging import jsonclass BaseHandler(tornado.web.RequestHandler): def write_error(self, status_code, **kwargs): """Sanitizes all error outputs to prevent Information Disclosure.""" self.set_header(‘Content-Type’, ‘application/json’)
# Internal logging of the actual traceback for debugging if "exc_info" in kwargs: logging.error(f"Error: {kwargs['exc_info']}") # Standardized, non-leaky error response response = { "error": { "code": status_code, "message": "An internal server error occurred." } } if status_code == 404: response["error"]["message"] = "Resource not found." elif status_code == 403: response["error"]["message"] = "Access denied." self.finish(json.dumps(response))
class SecureHandler(BaseHandler): def get(self): # This crash will now return a clean JSON error, not a trace raise Exception(“Critical DB failure”)
Your Tornado API
might be exposed to Improper Error Handling
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.