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&apos;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