Fix Shadow API Exposure in Tornado
Shadow APIs are the undocumented, 'dark' endpoints lurking in your Tornado Application manifest. They often stem from legacy debug handlers, forgotten internal tools, or dynamic routing patterns that lack visibility. In production, these are blind spots that bypass WAF rules and monitoring. If it's not in your OpenAPI spec, it's a liability. To fix shadow exposure, you must enforce explicit routing, environment-aware registration, and centralized authentication.
The Vulnerable Pattern
import tornado.webclass DebugConfigHandler(tornado.web.RequestHandler): def get(self): # Shadow API: Left active in production, leaks internal config self.write({“db_conn”: “secret_string”, “debug_mode”: True})
class UserHandler(tornado.web.RequestHandler): def get(self): self.write(“User Data”)
def make_app(): return tornado.web.Application([ (r”/api/v1/user”, UserHandler), (r”/internal/debug/config”, DebugConfigHandler), # Shadow endpoint ])
The Secure Implementation
The fix involves three layers of defense. First, 'Environment-Aware Routing' ensures that sensitive debug handlers are never even registered in the Tornado routing table when running in production. Second, the use of a 'BaseHandler' with a mandatory 'prepare()' method enforces a 'Secure by Default' posture, ensuring even undocumented routes require authentication. Finally, the application manifest should be used to programmatically generate an OpenAPI/Swagger schema, forcing developers to synchronize the code with the security inventory—if it's not in the schema, it shouldn't be in the code.
import tornado.web
import os
class BaseHandler(tornado.web.RequestHandler):
def prepare(self):
# Centralized Auth: Ensure no endpoint is ‘shadowed’ from security logic
if not self.request.headers.get(“Authorization”):
raise tornado.web.HTTPError(401)
class UserHandler(BaseHandler):
def get(self):
self.write(“User Data”)
def make_app():
# 1. Explicit Route Inventory
routes = [
(r”/api/v1/user”, UserHandler),
]
# 2. Environment-aware registration
if os.environ.get("APP_ENV") == "development":
from handlers.debug import DebugConfigHandler
routes.append((r"/local/debug", DebugConfigHandler))
return tornado.web.Application(routes, autoreload=False, compiled_template_cache=True)</code></pre>
Your Tornado API
might be exposed to Shadow API Exposure
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.