MEDIUM symlink attackfastapibasic auth

Symlink Attack in Fastapi with Basic Auth

Symlink Attack in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability

A Symlink Attack in a FastAPI service that uses HTTP Basic Authentication can occur when file-system operations are triggered based on attacker-controlled paths. FastAPI does not inherently create symlinks, but if your application accepts a filename or path parameter and uses it to open, read, or write files, an authenticated or unauthenticated caller can supply a crafted path that points to a sensitive location. When Basic Auth is in use, the endpoint may appear protected, but if authorization checks are incomplete or applied after path resolution, the symlink can redirect access to files outside the intended directory.

Consider an endpoint that writes a user-supplied filename to disk. If the code joins the supplied name to a base directory without canonicalization and then opens the file, an attacker can provide a path like ../../../etc/passwd or a symlink such as ../../../var/log/secrets.log. If the process runs with elevated privileges or the file is later read by another service, sensitive data can be disclosed. In a FastAPI route with Basic Auth, you might validate credentials and then call open(file_path) based on the parsed form data. The authentication check passes, but the path traversal occurs before or independently of the auth decision, creating a window for symlink abuse.

Another scenario involves file downloads or exports where the API streams a file to the client. If the server constructs a filesystem path from user input and uses a naive join, a symlink placed in the user-writable area can point to a critical system file. When the server opens the symlink target, it may inadvertently expose credentials, configuration, or logs. This risk is not eliminated by Basic Auth because the authentication layer only confirms identity, not authorization for the specific resource, and path canonicalization may be incomplete.

OpenAPI/Swagger specs that describe file-upload endpoints without clarifying path handling can obscure the danger. Runtime behavior discovered by a scanner may show that user-controlled paths reach filesystem operations without canonicalization or directory traversal checks, which a tool like middleBrick flags across 12 security checks including Input Validation and Property Authorization. The scan can surface related findings such as BOLA/IDOR when object ownership is not enforced, or Data Exposure when sensitive files are readable. These findings are mapped to real-world patterns, helping you understand how a seemingly isolated endpoint can contribute to a broader attack surface.

Examples of risky code include using os.path.join(UPLOAD_DIR, filename) without removing path traversal sequences or using Path(filename) without resolving to an absolute base. In FastAPI, a route that accepts a filename via FormData and streams a file can inadvertently allow symlink resolution if the path is not strictly confined. middleBrick’s LLM/AI Security checks do not apply here, but its Input Validation and Property Authorization checks highlight where user input reaches the filesystem and where authorization should be enforced more precisely.

Basic Auth-Specific Remediation in Fastapi — concrete code fixes

To mitigate symlink risks in FastAPI when using HTTP Basic Authentication, enforce strict path control and validate authorization before any filesystem interaction. Always resolve user-supplied paths against a canonical base directory and reject paths that escape that base. Do not rely on authentication alone to protect filesystem operations; combine it with explicit path validation and safe file-handling patterns.

Secure FastAPI route example with Basic Auth and path confinement

from fastapi import FastAPI, Depends, HTTPException, status, Form, Header
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import os
from pathlib import Path

app = FastAPI()
security = HTTPBasic()

# In a real app, use a secure user store; this is simplified for illustration
VALID_USERS = {"alice": "secret123", "bob": "password456"}

def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
    username = credentials.username
    password = credentials.password
    if username in VALID_USERS and VALID_USERS[username] == password:
        return username
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Incorrect username or password",
        headers={"WWW-Authenticate": "Basic"},
    )

BASE_DIR = Path("/srv/api-files").resolve()

def safe_join(base: Path, target: Path) -> Path:
    # Resolve and ensure the target is within base
    resolved = (base / target).resolve()
    if not str(resolved).startswith(str(base.resolve()) + os.sep) and resolved != base.resolve():
        raise ValueError("Path traversal attempt detected")
    return resolved

@app.post("/download/")
async def download_file(
    filename: str = Form(...),
    username: str = Depends(get_current_username)
):
    target = Path(filename)
    try:
        safe_path = safe_join(BASE_DIR, target)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid path")
    if not safe_path.is_file():
        raise HTTPException(status_code=404, detail="File not found")
    return {"path": str(safe_path)}

This example shows authentication via HTTP Basic, then canonicalizes and validates the user-supplied filename before any filesystem operation. The safe_join function resolves both the base and the joined path and ensures the result remains inside the allowed directory. Even if a symlink exists outside the base, the resolved path will be checked against the canonical base, preventing access to unintended files.

General Best Practices

  • Never trust user input for filesystem paths; always sanitize and resolve.
  • Use pathlib.Path.resolve() and strict prefix checks, not string starts-with, to avoid bypasses via encoded paths.
  • Run processes with minimal necessary permissions to reduce impact if a symlink does reach sensitive files.
  • Log rejected attempts for auditability without exposing sensitive path details to the client.

middleBrick’s CLI can be used to scan endpoints and confirm that file-related endpoints do not exhibit path traversal or symlink risks. With the Pro plan, continuous monitoring can be scheduled so that future changes to the API do not reintroduce unsafe path handling. The GitHub Action can fail builds if a security score drops, helping you catch regressions before deployment.

Frequently Asked Questions

Does HTTP Basic Authentication prevent symlink attacks?
No. Basic Auth confirms identity but does not protect filesystem paths. If your API resolves user-supplied paths without canonicalization or strict confinement, attackers can use symlinks or traversal sequences to access files outside the intended directory, regardless of authentication.
How can I test my FastAPI endpoints for symlink risks?
Send requests with crafted paths and symlink payloads to endpoints that handle files, and inspect responses for unauthorized access or information disclosure. Automated scanners like middleBrick include Input Validation checks that can surface path traversal and symlink exposure in unauthenticated attack surface scans.