Nosql Injection in Django with Basic Auth
Nosql Injection in Django with Basic Auth — how this specific combination creates or exposes the vulnerability
NoSQL injection in Django with Basic Auth occurs when user-controlled input is improperly handled by a NoSQL database query and the endpoint is protected only by HTTP Basic Authentication. Basic Auth provides a simple username:password header but does not alter how the application builds database queries; it only adds a transport-layer gate that can be bypassessed via leaked credentials, brute force, or misconfigured reverse proxies. If the view deserializes JSON or form data into query parameters and those values are concatenated into a NoSQL operator chain, an attacker can inject operators such as $where, $ne, or regex patterns to change query semantics.
In Django, this risk is commonly seen when integrating MongoDB or other document stores via libraries such as Djongo or MongoEngine. For example, a login handler might accept username and password fields and build a filter like User.objects(username=username, password=password). If the inputs are taken directly from request data without strict allowlists, an attacker can supply {"username": {"$ne": None}, "password": {"$exists": False}} to bypass authentication entirely. Because Basic Auth transmits credentials in an Authorization header rather than a session cookie, tokens or credentials may be inadvertently logged or cached, increasing exposure. Moreover, if the endpoint exposes stack traces or verbose errors, it can reveal collection or field names that aid further injection.
Middleware or decorators that enforce Basic Auth do not validate or sanitize input; they only ensure credentials are present. This mismatch means NoSQL injection flaws remain exploitable even when authentication is in place. Attack workflows commonly involve enumerating users via injection, extracting hashes or password patterns, and then pivoting to privilege escalation or data exfiltration. Because the scan category Authentication checks whether authentication is enforced, and the BOLA/IDOR category checks whether object-level authorization is applied per instance, a NoSQL injection that bypasses auth will often be flagged under both categories during a middleBrick scan.
Basic Auth-Specific Remediation in Django — concrete code fixes
Remediation focuses on strict input validation, parameterized queries, and avoiding string-based query building. Never concatenate user input directly into NoSQL query operators. Use Django form or serializer validation to enforce type, length, and pattern constraints. Treat Basic Auth as a transport credential mechanism and enforce additional protections such as rate limiting and short token lifetimes via middleware.
Example: Unsafe pattern with raw dictionary input
import json
from django.http import JsonResponse
def unsafe_login(request):
data = json.loads(request.body)
username = data.get('username', '')
password = data.get('password', '')
from myapp.models import User
# Dangerous: direct injection into NoSQL query
user = User.objects.find_one({'username': username, 'password': password})
if user:
return JsonResponse({'ok': True})
return JsonResponse({'error': 'Invalid'}, status=401)
Example: Safe parameterized query with allowlist validation
from django import forms
from django.http import JsonResponse
from django_auth_basic.decorators import basic_auth_required
from myapp.serializers import UserLoginSerializer
def safe_login(request):
serializer = UserLoginSerializer(data=request.POST)
if not serializer.is_valid():
return JsonResponse({'error': 'Invalid input'}, status=400)
cleaned = serializer.validated_data
from myapp.models import User
# Safe: fields are validated, no operator injection
user = User.objects.filter(username=cleaned['username'], password=cleaned['password']).first()
if user:
return JsonResponse({'ok': True})
return JsonResponse({'error': 'Invalid'}, status=401)
Example: Django form validation with Basic Auth decorator
from django import forms
from django.http import JsonResponse
from django_auth_basic.decorators import basic_auth_required
class LoginForm(forms.Form):
username = forms.CharField(max_length=150, regex=r'^[a-zA-Z0-9_.-]+$')
password = forms.CharField(min_length=8, max_length=128)
@basic_auth_required
def protected_login(request):
form = LoginForm(request.POST)
if not form.is_valid():
return JsonResponse({'error': 'Bad request'}, status=400)
username = form.cleaned_data['username']
password = form.cleaned_data['password']
from myapp.models import User
user = User.objects.filter(username=username, password=password).first()
if user:
return JsonResponse({'ok': True})
return JsonResponse({'error': 'Invalid'}, status=401)
Operational practices
- Use allowlists (regex or type constraints) for identifiers such as usernames and API keys.
- Prefer parameterized queries or an ORM that escapes special NoSQL operators.
- Log failed attempts without exposing stack traces or database structure.
- Combine Basic Auth with HTTPS and short-lived tokens to reduce credential leakage.
- Monitor for unusual query patterns that may indicate injection attempts.
By separating authentication from query construction and rigorously validating input, you reduce the attack surface for NoSQL injection even when Basic Auth is the only transport-side guard.