HIGH time of check time of usedjangobasic auth

Time Of Check Time Of Use in Django with Basic Auth

Time Of Check Time Of Use in Django with Basic Auth — how this specific combination creates or exposes the vulnerability

Time Of Check Time Of Use (TOCTOU) is a class of race condition where a system checks a condition (such as permissions) and later uses the result of that check to make a decision, but the state can change between the check and the use. In Django, combining Basic Authentication with authorization checks can create TOCTOU when permissions are validated separately from the actual resource access, especially when credentials are re-validated per request or when group/permission membership is mutable at runtime.

With Basic Auth, Django typically authenticates the user on each request using HTTPBasicAuthentication (from Django REST Framework) or via custom middleware that sets request.user. If an authorization check (for example, verifying group membership or a boolean flag on the user) occurs before a sensitive operation, and the user’s group or permissions are changed by another process between the check and the operation, the original check becomes stale. An attacker who can alter group membership or permissions (for example, through an administrative endpoint or a compromised account) can change state after the check but before the use, leading to unauthorized access or data exposure.

A concrete example: a view checks whether request.user.is_in_group('billing'), then proceeds to process or return a sensitive invoice. If an attacker triggers a concurrent update to the user’s group membership after the check passes but before the invoice data is accessed, the check no longer reflects the current state, and the sensitive data may be disclosed or modified. This is a TOCTOU because the authorization decision is split across time and relies on mutable backend state.

Django’s default user model stores permissions and groups in related database tables. If queries for permissions/groups are not performed atomically with the operation (for example, using select_for_update or ensuring checks happen immediately before use within the same transaction), race conditions can be exploited. Basic Auth exacerbates this when tokens or credentials are reused across requests, and the backend state is not locked or validated at the moment of use.

To detect this pattern, scans look for sequences where an authorization check (group/permission or attribute check) and the sensitive action are not performed within a single, atomic operation or transaction. Findings will highlight missing row-level locking, non-atomic check-then-act patterns, and use of mutable user attributes without re-validation at the point of use.

Basic Auth-Specific Remediation in Django — concrete code fixes

Remediation focuses on ensuring the authorization check and the use of the resource occur atomically and with up-to-date state. Avoid split checks and ensure permissions/groups are re-validated immediately before the operation, or use database-level constraints and locking to prevent state changes between check and use.

1) Use select_for_update within a transaction

When operating on sensitive resources, lock the relevant rows for the duration of the transaction so that group or permission changes cannot occur between check and use.

from django.db import transaction
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from myapp.models import Invoice

@login_required
def get_invoice(request, invoice_id):
    with transaction.atomic():
        # Lock the invoice row and re-check permission immediately before use
        invoice = Invoice.objects.select_for_update().get(pk=invoice_id)
        if not request.user.has_perm('billing.can_view_invoice', invoice):
            return JsonResponse({'error': 'Forbidden'}, status=403)
        # Safe to use invoice here
        return JsonResponse({'id': invoice.id, 'amount': invoice.amount})

2) Re-validate permissions immediately before use

Perform authorization checks as close as possible to the operation, avoiding earlier checks that can become stale.

from django.http import JsonResponse
from myapp.models import Document

def download_document(request, doc_id):
    # Authenticated by Basic Auth middleware earlier; re-validate at point of use
    document = Document.objects.get(pk=doc_id)
    if not request.user.has_object_permission(document, 'view'):
        return JsonResponse({'error': 'Forbidden'}, status=403)
    # Proceed with safe access
    return JsonResponse({'url': document.file_url})

3) Use Django’s built-in permission checks and avoid mutable group-based checks in critical paths

Prefer model-level permissions and object permissions that are evaluated at the query or instance level rather than group membership that can change concurrently.

from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import DetailView
from myapp.models import Report

class ReportDetailView(PermissionRequiredMixin, DetailView):
    model = Report
    permission_required = ('app.view_report',)
    # Ensure the permission check runs immediately before rendering

4) Employ row ownership and immutable audit fields where feasible

Design schemas so that sensitive operations rely on immutable ownership fields or tokens that cannot be changed by other processes mid-flow, reducing reliance on mutable group/permission checks.

from django.db import models

class SecureRecord(models.Model):
    owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    token = models.CharField(max_length=64)  # immutable per-record token
    # other fields…
    class Meta:
        indexes = []

5) API-level mitigation with DRF’s permission classes

If using Django REST Framework with Basic Auth, use permission classes that re-check object permissions on each request and avoid broad group checks.

from rest_framework import permissions

class InvoiceObjectPermission(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        # Re-validates on each request, preventing stale group checks
        return request.user.has_perm('billing.can_view_invoice', obj)

# Usage in a viewset
from rest_framework import viewsets
from myapp.models import Invoice
from myapp.serializers import InvoiceSerializer

class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Invoice.objects.all()
    serializer_class = InvoiceSerializer
    permission_classes = [InvoiceObjectPermission]

Frequently Asked Questions

Does middleBrick test for TOCTOU patterns in Basic Auth flows?
Yes. middleBrick’s Authorization and BOLA/IDOR checks include patterns that can reveal race conditions between authentication/authorization checks and resource use, including Basic Auth scenarios where permissions may change between check and use.
Can I rely on Basic Auth tokens alone to prevent TOCTOU?
No. Basic Auth provides transport-layer identity per request but does not prevent server-side state changes (e.g., group or permission updates). You must re-validate permissions atomically at the point of use and avoid split check-then-act logic.