Introduction
In a FastAPI application designed to monitor and control Tesla Powerwall systems, we discovered a critical denial-of-service vulnerability in app/main.py. The application exposed over 113 API endpoints—including sensitive control endpoints like /control/reserve and /control/mode—without any form of request throttling or rate limiting.
This meant that any attacker with network access to the application could send unlimited HTTP requests to any endpoint, potentially exhausting CPU, memory, and database connections. For an application controlling home energy storage systems, this vulnerability could disrupt critical powerwall monitoring and control operations, leaving users unable to manage their energy systems during peak demand or outages.
The vulnerable code pattern was straightforward: a FastAPI application with numerous routes but no middleware to limit request rates. While the application had proper authentication in place, it lacked the crucial defense-in-depth measure of rate limiting.
The Vulnerability Explained
What Makes This Dangerous?
The FastAPI application in app/main.py instantiated routes for various functionalities without any rate limiting middleware in the ASGI middleware stack. This created a situation where:
- Control endpoints (
/control/reserve,/control/mode) could be flooded with commands - Data endpoints (
/api/gateways/*,/api/timeseries/*) could be overwhelmed with read requests - Health endpoints (
/health) could be targeted to mask actual service degradation
The original application structure looked something like this:
from fastapi import FastAPI
app = FastAPI()
# 113+ endpoints defined without any rate limiting
@app.post("/control/reserve")
async def control_reserve():
# Critical powerwall control logic
pass
@app.post("/control/mode")
async def control_mode(mode: dict):
# Mode switching logic
pass
@app.get("/api/gateways/status")
async def gateway_status():
# Gateway status retrieval
pass
Attack Scenario
An attacker targeting this powerwall monitoring system could execute a denial-of-service attack with a simple script:
import asyncio
import aiohttp
async def flood_endpoint():
async with aiohttp.ClientSession() as session:
while True:
# Flood the control endpoint
await session.post("http://target/control/mode",
json={"mode": "self_consumption"})
This attack would:
1. Exhaust the server's ability to process legitimate requests
2. Potentially cause database connection pool exhaustion
3. Disrupt real-time powerwall monitoring during critical periods
4. Prevent legitimate users from controlling their energy systems
The DESIGN.md file explicitly documented this as a critical issue, noting that "a compromised token could flood the Powerwall with commands."
The Fix
The fix introduces a configurable, pure-ASGI rate limiting middleware called _RateLimitMiddleware that can be enabled via environment variables. This approach was chosen over the originally suggested slowapi route decorator for several important reasons:
Key Design Decisions
- Global application rather than per-route decoration
- Disabled by default to avoid breaking existing integrations (Powerwall-Dashboard, Grafana, Home Assistant polling)
- Configurable via environment variables for flexibility
The updated DESIGN.md reflects this resolution:
#### 1. ✅ No Rate Limiting on Control Endpoints — Addressed
**Location**: [app/api/legacy.py](app/api/legacy.py#L73-L87), [app/main.py](app/main.py) (`_RateLimitMiddleware`)
**Resolution**: A first-party, pure-ASGI, fixed-window rate limiter is available,
applied globally (not just `/control/*`) rather than the originally-suggested
`slowapi` route decorator. It is **disabled by default** (`PW_RATE_LIMIT_ENABLED`)
so it never regresses the common Powerwall-Dashboard/Grafana/Home Assistant polling
use case.
Configuration Options
The middleware is controlled through these environment variables:
| Variable | Purpose |
|---|---|
PW_RATE_LIMIT_ENABLED |
Enable/disable rate limiting (default: off) |
PW_RATE_LIMIT_MAX_REQUESTS |
Maximum requests per window |
PW_RATE_LIMIT_WINDOW_SECONDS |
Time window duration |
PW_RATE_LIMIT_MAX_BUCKETS |
Maximum tracked client buckets (memory bound) |
Before vs. After
Before: No rate limiting middleware in the ASGI stack
app = FastAPI()
# All 113+ endpoints accessible without throttling
After: Configurable rate limiting middleware available
app = FastAPI()
# _RateLimitMiddleware added to middleware stack
# Controlled via PW_RATE_LIMIT_ENABLED environment variable
# Returns HTTP 429 when limits exceeded
The fix also updated the priority table in DESIGN.md:
| Priority | Area | Effort |
|----------|------|--------|
| ~~High~~ Done | ~~Rate limiting on control endpoints~~ Available via `PW_RATE_LIMIT_ENABLED` (default off) | Low |
Prevention & Best Practices
1. Always Include Rate Limiting in API Design
Rate limiting should be a first-class consideration when designing APIs, not an afterthought. Consider:
- Endpoint sensitivity: Control endpoints need stricter limits than read-only endpoints
- User tiers: Different rate limits for different authentication levels
- Burst vs. sustained: Allow short bursts while limiting sustained traffic
2. Defense in Depth
The fix documentation wisely recommends pairing application-level rate limiting with infrastructure-level protection:
"the recommendation to pair this with a real reverse-proxy rate limiter for internet-exposed deployments"
Consider implementing rate limiting at multiple layers:
- Application level: FastAPI middleware (as implemented here)
- Reverse proxy: Nginx, HAProxy, or cloud load balancers
- CDN/WAF: Cloudflare, AWS WAF, or similar services
3. Configuration Best Practices
The fix demonstrates excellent configuration practices:
# Environment-variable driven configuration
# Allows different limits for dev/staging/production
# Disabled by default to avoid breaking existing integrations
4. Memory Management
The PW_RATE_LIMIT_MAX_BUCKETS configuration prevents memory exhaustion from tracking too many unique clients—an important consideration that prevents the rate limiter itself from becoming a DoS vector.
Key Takeaways
- All 113+ endpoints in
app/main.pywere vulnerable to unlimited request flooding before this fix - Control endpoints like
/control/reserveand/control/modeposed the highest risk as they directly affect powerwall operations - The fix uses a fixed-window algorithm with configurable parameters via environment variables
- Disabled by default preserves backward compatibility with existing monitoring integrations
- Memory is bounded via
PW_RATE_LIMIT_MAX_BUCKETSto prevent the rate limiter from becoming an attack vector itself
How Orbis AppSec Detected This
- Source: HTTP requests from any network client with access to the application
- Sink: All 113+ API endpoints in
app/main.pyincluding/control/reserve,/control/mode,/api/gateways/*, and/api/timeseries/* - Missing control: No rate limiting middleware in the ASGI middleware stack to throttle incoming requests
- CWE: CWE-770 (Allocation of Resources Without Limits or Throttling)
- Fix: Implemented
_RateLimitMiddlewareas a configurable, pure-ASGI fixed-window rate limiter controlled viaPW_RATE_LIMIT_ENABLEDenvironment variable
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This vulnerability demonstrates why rate limiting is a critical security control for any API, especially those controlling physical systems like energy storage. The fix provides a flexible, opt-in solution that protects against denial-of-service attacks while maintaining compatibility with existing monitoring integrations.
When building APIs with FastAPI or any other framework, always consider:
1. What happens if an endpoint receives thousands of requests per second?
2. Which endpoints are most critical and need the strictest limits?
3. How will rate limiting interact with legitimate high-frequency clients?
The configurable approach taken in this fix—disabled by default with environment variable controls—provides a template for implementing rate limiting in applications with diverse deployment scenarios.