Middlewares
Three middlewares ship with 0-mcp. Each does one thing well, runs in a specific order, and stays out of your way once installed.
Recommended order
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# ... your other middleware ...
'zeromcp.SecurityMiddleware', # 1. block scanners early
'zeromcp.AuthMiddleware', # 2. resolve session for non-resource views
'zeromcp.ExceptionMiddleware', # 3. render HTTPException as JSON
]Order matters. Security runs first so blocked IPs cannot reach the auth middleware. Auth runs before your views so non-0-mcp views can read the resolved session. Exception runs last so it can catch errors raised by anything above.
Two protection layers
0-mcp uses two related but distinct layers:
- SecurityMiddleware — edge blocking before any view runs.
- BaseResource dispatch rate limiting — application-layer throttling for 0-mcp resources.
Both layers share the same blocked-IP store in Redis (rate_limit:blocked:<ip>), so a block raised by one layer is seen by the other.
SecurityMiddleware
Stops scanners and abusers before any view runs.
On every request, it checks:
- The request path against
BLOCK_PATTERNS— regexes that match scanner targets (/wp-admin,/.env,/.git,/phpmyadmin), traversal (../), SQL injection payloads, XSS in the URL. - The user agent against
BLOCK_USER_AGENTS— names of known scanners (sqlmap,nikto,nuclei,masscan,acunetix). - The IP's recent 4xx response count — sliding windows track 10 per minute and 30 per hour. Either window trips → block.
When any rule fires, the middleware:
- Adds the IP to
rate_limit:blocked:<ip>in Redis with a 24-hour TTL. - Returns 403 immediately.
From that moment, every request from that IP returns 403 instantly — before authentication, before views, before any DB lookup. Your logs get cleaner. Your DB stops processing junk. Real users never notice.
The middleware is intentionally aggressive. False positives are rare on REST APIs because real clients do not visit /wp-login.php or send a User-Agent: sqlmap header.
AuthMiddleware
Resolves the session from the cookie and exposes it on the request.
This middleware is the entry point for any non-0-mcp view that wants to know who the user is. It does not enforce authentication — that is BaseResource's job. It just resolves the session and attaches it.
On every request:
- Reads the cookie named after
COOKIE_ID. - Validates the value against the session-key regex. Malformed → no session.
- Looks up
sessions:<key>in Redis. - On hit, parses the session JSON and exposes:
request.user # session['user'] request.account # session['account'] request.account_id # session['account']['id'] request.session # the full dict request.authenticated # True/False
- Switches the active database connection to the tenant via
aset_tenant(account_id).
Resources do their own session lookup inside dispatch — so BaseResource does not depend on this middleware. You only need it for non-0-mcp views (a Django template view, a custom function-based view, an admin redirect) that should know about the session.
AuthMiddleware does not handle X-Api-Key. API keys are resolved inside the resource's dispatch. Non-resource views authenticated by API key need to call get_api_session(key) themselves.
ExceptionMiddleware
Turns HTTPException into a JSON response.
Whenever any view raises HTTPException(status, detail), this middleware catches it and renders:
{
"success": false,
"status": 403,
"detail": "Admins only"
}The HTTP status matches the first argument. Anything that is not an HTTPException is left alone — Django's normal error handling kicks in.
Inside resources, BaseResource.dispatch already catches unhandled exceptions and returns sanitized 500s. This middleware exists for the rare case where an HTTPException escapes a non-resource view.
Disabling individual middlewares
Each middleware is independent. Drop the lines you don't need:
- No multi-tenant non-resource views? Skip
AuthMiddleware. - Don't use
HTTPExceptionoutside resources? SkipExceptionMiddleware. - On a fully-trusted internal network? You can technically skip
SecurityMiddleware— but it's free, so don't.
0-mcp by Stamatios Stamou Jr — github.com/ssjunior/0-mcp