Securing ML APIs
ML APIs face unique security threats beyond traditional web APIs — including model inversion attacks, adversarial inputs, and intellectual property theft — demanding a defence-in-depth security strategy.
Authentication and Authorization
All production ML APIs must require authentication. API keys are the simplest option; JWT tokens provide stateless, scoped access for multi-user systems.
API Key Authentication in FastAPI
<pre><code class="language-python">from fastapi import FastAPI, HTTPException, Security
from fastapi.security import APIKeyHeader
import secrets
app = FastAPI()
API_KEY_NAME = "X-API-Key"
VALID_API_KEY = "super-secret-key-stored-in-env"
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
def require_api_key(api_key: str = Security(api_key_header)):
if api_key is None or not secrets.compare_digest(api_key, VALID_API_KEY):
raise HTTPException(status_code=403, detail="Invalid or missing API key")
return api_key
@app.post("/predict")
def predict(features: list, api_key: str = Security(require_api_key)):
# Only authenticated callers reach here
return {"prediction": 0}</pre>
Rate Limiting
<pre><code class="language-python">from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
from fastapi import Request
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.post("/predict")
@limiter.limit("60/minute") # max 60 requests per minute per IP
async def predict(request: Request, features: list):
return {"prediction": 0}</pre>
Input Validation and Output Sanitization
Adversarial and malformed inputs can cause model misbehaviour or information leakage. Strict validation at the boundary prevents most injection attacks.
Strict Input Schema Enforcement
<pre><code class="language-python">from pydantic import BaseModel, Field, field_validator
import math
class SecurePredictRequest(BaseModel):
features: list[float] = Field(..., min_length=4, max_length=4)
@field_validator("features")
@classmethod
def no_inf_or_nan(cls, v):
if any(math.isnan(x) or math.isinf(x) for x in v):
raise ValueError("Feature values must be finite")
if any(abs(x) > 1e6 for x in v):
raise ValueError("Feature values out of expected range")
return v</pre>
Output Sanitization and Information Hiding
- Truncate probabilities: Return rounded 2-decimal probabilities rather than full-precision values to hinder model inversion attacks
- Suppress raw scores: Return only the predicted class label when confidence scores aren't needed by the client
- Avoid error detail leakage: Return generic error messages (never stack traces or internal feature names) to external callers
- Log all requests: Maintain a full audit trail for anomaly detection and post-incident forensics
Adversarial Attack Awareness
ML-specific attacks go beyond standard web threats — understanding them is the first step to building defences.
Common ML API Attacks
- Model Inversion: Repeated queries reconstruct training data — mitigate with output rounding and rate limiting
- Membership Inference: Determine if a record was in the training set — mitigate with differential privacy and confidence thresholding
- Model Extraction (Stealing): Clone the model by querying it systematically — mitigate with rate limits, CAPTCHA, and query budget enforcement
- Adversarial Examples: Crafted inputs that fool the model — mitigate with input pre-processing (feature squeezing, input smoothing) and adversarial training