Nosql Injection in Fastapi with Basic Auth
Nosql Injection in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Nosql Injection occurs when attacker-controlled input is interpreted as part of a NoSQL query, allowing unintended data access or modification. In Fastapi, using HTTP Basic Auth for authentication does not inherently protect against injection; it only provides a transport-layer identity for the request. If an endpoint builds NoSQL queries by concatenating user-supplied JSON or query parameters, the authentication layer does not sanitize or validate those values.
Consider a Fastapi route that retrieves a user profile by username. With Basic Auth present, the developer may assume the authenticated identity is trustworthy and directly incorporate parsed JSON body fields into a PyMongo or similar NoSQL query. For example, merging request.auth with unchecked body input can lead to query manipulation. An attacker authenticated with a low-privilege account can supply a payload such as {'$where': 'true'} or use operator injection like {'$ne': None} to change query semantics. Because the route trusts the combined input, the database executes the malicious structure, exposing other users’ data or enabling data exfiltration.
Another common pattern is using dictionary unpacking to forward filters to a find operation. If the code does not validate or sanitize keys and values, an authenticated user can inject operators that bypass intended filters. For instance, a search route that builds { 'username': username, **extra_filters } may allow an authenticated attacker to append {'$regex': '.*', '$ne': ''} to extract more records than intended. The combination of Fastapi’s ease of parsing JSON bodies and the flexibility of NoSQL operators creates a surface where injection is feasible despite the presence of Basic Auth.
OpenAPI/Swagger spec analysis helps identify such risks by revealing which parameters are passed into database operations. When specs define request bodies that map directly to query structures without validation schemas, runtime testing can detect whether injected operators reach the backend. The scanner performs 12 security checks in parallel, including Input Validation and Unsafe Consumption, to highlight whether endpoints safely handle NoSQL constructs. This ensures that even when Basic Auth is used, the application does not inadvertently trust raw input in query construction.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
To mitigate Nosql Injection in Fastapi with Basic Auth, validate and sanitize all user-controlled data before it reaches the database. Use structured Pydantic models for input, apply strict type constraints, and avoid directly passing dictionaries derived from user input into query operators. Below are concrete code examples demonstrating secure patterns.
First, a minimal Fastapi app with HTTP Basic Auth:
from fastapi import Fastapi, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import re
app = Fastapi()
security = HTTPBasic()
def verify_auth(credentials: HTTPBasicCredentials = Depends(security)):
# Replace with secure credential verification
if credentials.username != 'admin' or credentials.password != 's3cur3P@ss!':
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Incorrect username or password',
headers={'WWW-Authenticate': 'Basic'}
)
return credentials.username
Next, a safe endpoint that avoids injection by using a Pydantic model and explicit projection instead of dynamic dictionary merging:
from fastapi import Depends
from pydantic import BaseModel, Field
from typing import Optional
class ProfileQuery(BaseModel):
username: str = Field(..., min_length=1, max_length=64, pattern=r'^[a-zA-Z0-9_]+$')
@app.get('/profile')
def get_profile(query: ProfileQuery, username: str = Depends(verify_auth)):
# Safe: explicit field mapping, no direct dictionary unpacking into query
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017')
collection = client.db.profiles
filter_spec = {'username': query.username}
result = collection.find_one(filter_spec, {'email': 0, 'password': 1})
return {'username': result['username'], 'role': result.get('role')}
If filtering by additional optional fields, validate each field individually and map to allowed operators rather than forwarding raw input:
from typing import List
class SearchQuery(BaseModel):
roles: Optional[List[str]] = None
min_score: Optional[int] = None
def build_filter(roles: Optional[List[str]], min_score: Optional[int]) -> dict:
filter_spec = {}
if roles:
# Allowlist validation for enum-like values
allowed_roles = {'admin', 'user', 'guest'}
validated = [r for r in roles if r in allowed_roles]
if validated:
filter_spec['role'] = {'$in': validated}
if min_score is not None:
if not isinstance(min_score, int) or min_score < 0 or min_score > 100:
raise HTTPException(status_code=400, detail='min_score must be an integer 0–100')
filter_spec['score'] = {'$gte': min_score}
return filter_spec
@app.get('/search')
def search_profiles(query: SearchQuery = Depends(), username: str = Depends(verify_auth)):
filter_spec = build_filter(query.roles, query.min_score)
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017')
collection = client.db.profiles
cursor = collection.find(filter_spec)
return [{'username': doc['username'], 'role': doc['role']} for doc in cursor]
These patterns ensure that user input is validated, constrained, and never directly concatenated into NoSQL queries. Even with Basic Auth present, the application treats authenticated identity separately from data access logic, reducing the attack surface for Nosql Injection.