SecurityMiddleware¶
The SecurityMiddleware class is the core component of FastAPI Guard that handles all security features.
Architecture (v4.2.0+)
Since v4.2.0, SecurityMiddleware uses a modular architecture with specialized core modules. The middleware acts as an orchestration layer, delegating to specialized handlers. See Core Architecture for internal details.
Class Definition¶
class SecurityMiddleware(BaseHTTPMiddleware):
def __init__(
self,
app: Callable[[Request], Awaitable[Response]],
config: SecurityConfig
):
"""
Initialize the SecurityMiddleware.
Args:
app: The FastAPI/Starlette application
config: Security configuration object
Note:
The middleware initializes all core components using
dependency injection for clean separation of concerns.
"""
Architecture Overview¶
Request Processing Flow¶
The middleware processes requests through a modular pipeline:
Request → Middleware.dispatch()
↓
1. BypassHandler.handle_passthrough()
├─ No client IP? → Pass through
└─ Excluded path? → Pass through
↓
2. Extract client IP and route config
↓
3. BypassHandler.handle_security_bypass()
└─ Decorator bypass? → Pass through
↓
4. SecurityCheckPipeline.execute()
├─ RouteConfigCheck
├─ EmergencyModeCheck
├─ HttpsEnforcementCheck
├─ RequestLoggingCheck
├─ RequestSizeContentCheck
├─ RequiredHeadersCheck
├─ AuthenticationCheck
├─ ReferrerCheck
├─ CustomValidatorsCheck
├─ TimeWindowCheck
├─ CloudIpRefreshCheck
├─ IpSecurityCheck
├─ CloudProviderCheck
├─ UserAgentCheck
├─ RateLimitCheck
├─ SuspiciousActivityCheck
└─ CustomRequestCheck
↓
5. BehavioralProcessor.process_usage_rules()
↓
6. call_next(request) # Forward to app
↓
7. ErrorResponseFactory.process_response()
├─ Apply security headers
├─ Apply CORS headers
├─ Collect metrics
└─ Process behavioral return rules
↓
Response
Core Components¶
The middleware delegates to these specialized modules:
- SecurityCheckPipeline: Executes security checks in sequence
- SecurityEventBus: Sends security events to monitoring agent
- MetricsCollector: Collects request/response metrics
- HandlerInitializer: Initializes Redis and Agent handlers
- ErrorResponseFactory: Creates and processes responses
- RouteConfigResolver: Resolves decorator configurations
- RequestValidator: Validates request properties
- BypassHandler: Handles security bypasses
- BehavioralProcessor: Processes behavioral rules
See Core Architecture for detailed documentation of each module.
Public Methods¶
dispatch¶
async def dispatch(
self,
request: Request,
call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
"""
Main request handler that orchestrates security checks.
This method is pure orchestration - it delegates all logic
to specialized handlers and maintains no business logic itself.
Args:
request: The incoming request
call_next: The next middleware/handler in the chain
Returns:
Response: Either from security checks (blocking) or from the app
Flow:
1. Handle passthrough cases (no client, excluded paths)
2. Get route config and client IP
3. Handle security bypasses
4. Execute security pipeline
5. Process behavioral usage rules
6. Call next handler
7. Process response (headers, metrics, behavioral return rules)
"""
create_error_response¶
async def create_error_response(
self,
status_code: int,
default_message: str
) -> Response:
"""
Create standardized error responses.
Delegates to ErrorResponseFactory for response creation.
Args:
status_code: HTTP status code
default_message: Default error message
Returns:
Response: Error response with optional custom message
Note:
Custom error messages can be configured in SecurityConfig
via the custom_error_responses dict.
"""
initialize¶
async def initialize(self) -> None:
"""
Initialize all components asynchronously.
Tasks performed:
- Build security check pipeline
- Initialize Redis handlers (if enabled)
- Initialize agent integrations (if enabled)
- Initialize dynamic rule manager (if configured)
"""
initialize is part of the public API for advanced integration scenarios, but most applications should not call it directly. The recommended path is to wire FastAPI Guard's lifespan helper at app construction time:
from fastapi import FastAPI
from guard.lifespan import guard_lifespan
from guard.middleware import SecurityMiddleware
from guard import SecurityConfig
config = SecurityConfig(enable_redis=True, redis_url="redis://localhost:6379")
app = FastAPI(lifespan=guard_lifespan)
app.add_middleware(SecurityMiddleware, config=config)
If you already have a custom lifespan, compose them with make_lifespan:
from contextlib import asynccontextmanager
from guard.lifespan import make_lifespan
@asynccontextmanager
async def my_lifespan(app):
yield
app = FastAPI(lifespan=make_lifespan(my_lifespan))
app.add_middleware(SecurityMiddleware, config=config)
The lifespan helpers warm guard-core's singletons (cloud-IP cache, IP ban store, suspicious patterns, Redis pool) AND populate a shared-state registry so the live request-handling middleware adopts the spawned instance's pipeline, agent handler, and event bus by reference. This guarantees composite_handler.start() runs exactly once per config — no duplicate OTEL set_tracer_provider already set warning, no leaked agent worker tasks.
mark_initialized¶
mark_initialized is the public setter the lifespan helpers call after they finish warming or adopting state. It flips the internal initialization flag so the first request does not re-trigger lazy init. User code rarely needs to call this directly; the lifespan helpers handle it.
set_decorator_handler¶
def set_decorator_handler(
self,
decorator_handler: BaseSecurityDecorator | None
) -> None:
"""
Set the SecurityDecorator instance for decorator support.
This enables route-level security configuration via decorators.
Args:
decorator_handler: SecurityDecorator instance or None
Example:
```python
guard_deco = SecurityDecorator(config)
middleware.set_decorator_handler(guard_deco)
# Or set on app state:
app.state.guard_decorator = guard_deco
```
"""
State Sharing via the Shared-State Registry¶
FastAPI Guard maintains a module-local registry at guard._middleware_state, keyed by id(config). The registry holds a MiddlewareState dataclass containing the live security_pipeline, composite_handler, event_bus, metrics_collector, response_factory, validator, bypass_handler, behavioral_processor, handler_initializer, and agent_handler.
When multiple SecurityMiddleware instances are constructed against the same SecurityConfig — the common case under the lifespan helpers (where one instance is spawned to perform warmup while Starlette later constructs the live request-handling instance), and the sub-app mounted-middleware case — every instance after the first adopts the registered components by reference instead of building its own.
The keying is intentional: two SecurityConfig instances with identical contents but separate id() values get separate registry entries, because they are logically distinct configurations. If you want shared state, share the config object.
The practical effect is that composite_handler.start() — which sets the OTEL/Logfire global tracer providers and starts the agent worker tasks — runs exactly once per config object. No duplicate set_tracer_provider already set warning, no leaked agent worker tasks, no duplicate buffered events.
Handler Integration¶
The middleware works with singleton handler instances:
- All handler classes (IPBanManager, CloudManager, etc.) use the singleton pattern
- The middleware initializes these existing instances conditionally based on configuration
- IPInfoManager is only initialized when country filtering is enabled
- CloudManager is only loaded when cloud provider blocking is configured
- This selective loading improves performance when not all features are used
Initialization Process¶
The middleware uses HandlerInitializer to set up all handlers:
# In __init__
self.handler_initializer = HandlerInitializer(
config=self.config,
redis_handler=self.redis_handler,
agent_handler=self.agent_handler,
geo_ip_handler=self.geo_ip_handler,
rate_limit_handler=self.rate_limit_handler,
guard_decorator=self.guard_decorator,
)
# In initialize()
await self.handler_initializer.initialize_redis_handlers()
await self.handler_initializer.initialize_agent_integrations()
Redis Configuration¶
Enable Redis in SecurityConfig:
config = SecurityConfig(
enable_redis=True,
redis_url="redis://prod:6379/0",
redis_prefix="prod_security:"
)
The middleware automatically initializes:
- CloudManager cloud provider IP ranges
- IPBanManager distributed banning
- IPInfoManager IP geolocation
- RateLimitManager rate limiting
- RedisManager Redis caching
- SusPatternsManager suspicious patterns
Proxy Security Configuration¶
The middleware supports secure handling of proxy headers:
config = SecurityConfig(
trusted_proxies=["10.0.0.1", "192.168.1.0/24"], # List of trusted proxy IPs/ranges
trusted_proxy_depth=1, # Number of proxies in the chain
trust_x_forwarded_proto=True, # Trust X-Forwarded-Proto header from trusted proxies
)
This prevents IP spoofing attacks through header manipulation.
Usage Examples¶
Basic Setup¶
from fastapi import FastAPI
from guard.middleware import SecurityMiddleware
from guard import SecurityConfig
app = FastAPI()
config = SecurityConfig(
rate_limit=100,
enable_https=True,
enable_cors=True
)
app.add_middleware(SecurityMiddleware, config=config)
With Decorators¶
from fastapi import FastAPI
from guard.middleware import SecurityMiddleware
from guard import SecurityConfig
from guard import SecurityDecorator
app = FastAPI()
config = SecurityConfig(rate_limit=100)
guard_deco = SecurityDecorator(config)
# Apply decorators to routes
@app.get("/api/limited")
@guard_deco.rate_limit(requests=10, window=300)
def limited_endpoint():
return {"data": "limited"}
# Add middleware and set decorator
app.add_middleware(SecurityMiddleware, config=config)
app.state.guard_decorator = guard_deco # Required for decorator integration
With Eager Initialization (Lifespan)¶
from fastapi import FastAPI
from guard.lifespan import guard_lifespan
from guard.middleware import SecurityMiddleware
from guard import SecurityConfig
config = SecurityConfig(
enable_redis=True,
redis_url="redis://localhost:6379"
)
app = FastAPI(lifespan=guard_lifespan)
app.add_middleware(SecurityMiddleware, config=config)
guard_lifespan runs the full initialization sequence at ASGI startup so the first request hits a pre-warmed middleware. Use make_lifespan(existing) to compose with your own lifespan context manager.
Internal Architecture¶
For Contributors
The internal architecture is documented in Core Architecture. This section provides a high-level overview.
Modular Design (v4.2.0+)¶
The middleware delegates to specialized modules in guard_core/core/:
- checks/: Security check implementations (Chain of Responsibility pattern)
- events/: Event bus and metrics collection
- initialization/: Handler initialization logic
- responses/: Response creation and processing
- routing/: Route configuration resolution
- validation/: Request validation utilities
- bypass/: Security bypass handling
- behavioral/: Behavioral rule processing
Benefits of Modular Architecture¶
- Maintainability: Each module < 200 LOC, single responsibility
- Testability: Each component independently testable
- Performance: Better caching and optimization opportunities
- Extensibility: Easy to add new checks or modify behavior
- Development Speed: 2-3x faster feature additions (projected)
See Also¶
- Core Architecture - Detailed internal architecture
- SecurityConfig - Configuration options
- Decorators - Route-level security
- API Overview - Complete API reference