HIGH symlink attackdjangomutual tls

Symlink Attack in Django with Mutual Tls

Symlink Attack in Django with Mutual Tls — how this specific combination creates or exposes the vulnerability

A symlink attack involves an attacker forcing a program to access a file path that resolves through a symbolic link to a location the program did not intend to access. In Django, this commonly surfaces when user-controlled input is used to build file paths for uploads or downloads without canonicalization or strict validation. When mutual TLS (mTLS) is used, the server authenticates the client via a client certificate, which can establish strong identity and authorization at the transport layer. However, mTLS does not inherently protect the application from logic flaws such as path traversal or symlink-based file access; it only ensures that the request originates from a trusted client. This can create a false sense of security: an authenticated client with a valid certificate may still send parameters that lead Django to follow symlinks if file handling is implemented unsafely.

Consider a Django view that serves user-uploaded documents and uses the client certificate’s common name (CN) to build a storage path. If the view concatenates the CN directly into the filesystem path and uses functions like open() or os.path.join() without resolving symlinks, an attacker who possesses a valid certificate can craft filenames containing sequences such as ../../../etc/passwd or embed a symlink in the upload directory that points outside the intended directory. Because mTLS terminates TLS and provides client identity, the request passes authentication checks, but the application’s file resolution logic may resolve the symlink and expose or overwrite sensitive files. The combination therefore exposes a BOLA/IDOR-like file access vector where authorization is enforced at the certificate level but not at the path level, and data exposure can occur if sensitive files are reachable via symlink resolution.

Moreover, in scenarios where Django serves media through a web server (e.g., Nginx) configured to trust the proxy, an attacker might leverage symlinks in uploaded content to cause the web server to serve files it should not, complementing the mTLS-authenticated API endpoint. The scanner categories relevant here include Authentication (mTLS verifies client certs), BOLA/IDOR (improper authorization on file access), and Data Exposure (sensitive files accessible via symlink). middleBrick detects such risks by correlating runtime behavior with OpenAPI specifications and highlighting insecure file handling patterns even when mTLS is in use.

Mutual Tls-Specific Remediation in Django — concrete code fixes

To mitigate symlink attacks in Django while using mutual TLS, you must enforce strict path validation and canonicalization independent of transport-layer authentication. Do not rely on mTLS to prevent file path manipulation. Always resolve user-controlled path components against a base directory using functions that eliminate traversal sequences and symlinks, such as os.path.realpath or pathlib’s resolve(). Validate that the resolved path remains within the intended directory tree before performing any file operations.

Below are concrete code examples for a Django view that serves files with mTLS client identification but without unsafe path handling.

import os
from pathlib import Path
from django.conf import settings
from django.http import HttpResponse, Http404
from django.views import View

class SecureFileView(View):
    # Base directory where user files are stored
    BASE_DIR = Path(settings.MEDIA_ROOT) / 'user_uploads'

    def get(self, request, file_path):
        # client cert info is available in request.META when mTLS is terminated by the web server
        client_dn = request.META.get('SSL_CLIENT_S_DN_CN', 'unknown')

        # Canonicalize and restrict to BASE_DIR
        try:
            target = (self.BASE_DIR / file_path).resolve(strict=True)
        except (FileNotFoundError, RuntimeError):
            raise Http404('File not found')

        # Ensure the resolved path is still inside BASE_DIR
        if not self._is_path_within_base(target, self.BASE_DIR):
            raise Http404('Invalid path')

        # Serve file safely
        with open(target, 'rb') as f:
            content = f.read()
        return HttpResponse(content, content_type='application/octet-stream')

    @staticmethod
    def _is_path_within_base(path: Path, base: Path) -> bool:
        try:
            path.relative_to(base)
            return True
        except ValueError:
            return False

In this example, (self.BASE_DIR / file_path).resolve(strict=True) eliminates any ., .., or symlinks in the path and raises an error if the file does not exist. The subsequent check with _is_path_within_base ensures that even if the resolver is somehow bypassed, the final path cannot escape the designated directory. The client certificate DN is logged or used for audit purposes but never for path construction, preventing authorization bypass via mTLS identity assumptions.

Additionally, configure your web server (e.g., Nginx) to deny symlink following for locations serving user-uploaded content. For Django management commands that process user-provided paths, apply the same canonicalization logic and avoid direct use of os.path.join with untrusted input. middleBrick’s scans can validate these mitigations by checking for insecure file handling patterns and improper use of path resolution even when mTLS is present in the API definition.

Frequently Asked Questions

Does mutual TLS prevent symlink attacks in Django?
No. Mutual TLS authenticates the client at the transport layer but does not protect against application-level path traversal or symlink resolution. You must validate and canonicalize file paths independently.
What should I validate when serving files in Django with mTLS?
Always resolve user-controlled path components with os.path.realpath or pathlib.Path.resolve, ensure the result is within your intended base directory, and never use raw user input to build filesystem paths.