Container Escape in Fastapi with Api Keys
Container Escape in Fastapi with Api Keys — how this specific combination creates or exposes the vulnerability
A container escape in a FastAPI service that relies only on API keys occurs when an authenticated API request is able to interact with the host system or orchestration layer in unintended ways. FastAPI does not enforce container boundaries; if the application logic trusts the API key value to authorize sensitive host-level operations, an attacker who obtains or manipulates the key can leverage the trusted path to mount an escape attempt.
Consider a FastAPI app that maps API keys to tenant IDs and uses the key to decide which filesystem paths are accessible. If the API key is treated as authoritative for access control and the code builds shell commands or file paths using unchecked key-derived input, an attacker can attempt path traversal or command injection to break out of the containerized workload. For example, suppose the API key is interpreted as a directory name and concatenated into a system command without validation:
import subprocess
from fastapi import FastAPI, Depends, HTTPException
app = FastAPI()
# Example insecure mapping: key -> tenant path prefix
API_KEY_TO_PATH = {
"tenant_alpha": "/data/tenant_alpha",
"tenant_beta": "/data/tenant_beta",
}
def get_tenant_path(api_key: str):
path = API_KEY_TO_PATH.get(api_key)
if not path:
raise HTTPException(status_code=401, detail="Invalid API key")
return path
@app.get("/files/{filename}")
async def read_file(filename: str, api_key: str, tenant_path: str = Depends(get_tenant_path)):
# Vulnerable: filename is concatenated without validation
full_path = f"{tenant_path}/{filename}"
# If an attacker can control filename and the API key maps to a host path,
# they may traverse outside the intended directory.
result = subprocess.check_output(["cat", full_path])
return {"content": result.decode()}
If the container runs with elevated capabilities or the mounted host filesystem exposes sensitive paths, an attacker supplying filename such as ../../../etc/passwd could read host files. While this example highlights path traversal, a container escape typically requires a chain that includes host access; API keys alone rarely enable escape unless they grant permissions to execute privileged operations or interact with the container runtime.
Another scenario involves an API key–protected endpoint that triggers an action assumed to be safe because it runs inside the container. If FastAPI passes the key-derived context to an orchestrator call (e.g., via environment variables or injected secrets), and the container is misconfigured with shared namespaces or privileged options, an exploit chain might leverage SSRF or insecure deserialization to reach the orchestration layer. The API key becomes a credential that authorizes the malicious workflow, effectively turning an authenticated request into an escape vector.
Because middleBrick scans unauthenticated attack surfaces, it can detect endpoints that accept API keys and then probe for host path interactions, command injection, or SSRF that could contribute to an escape. Findings will highlight risky concatenation, missing input validation, and overly broad permissions rather than asserting escape success, since detection does not imply container compromise.
Api Keys-Specific Remediation in Fastapi — concrete code fixes
Remediation centers on strict input validation, avoiding direct use of API key values in system commands or filesystem paths, and enforcing least privilege within the container. Do not rely on API keys alone to authorize host-level actions; treat them as authentication tokens, not authorization proofs for filesystem or process control.
Use path-safe operations such as os.path.realpath and allowlists, and avoid shelling out to system utilities. Below is a hardened FastAPI example that validates filenames against an allowlist and uses safe path resolution instead of concatenation:
import os
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
app = FastAPI()
API_KEY_TO_TENANT = {
"tenant_alpha": "alpha",
"tenant_beta": "beta",
}
# Allowlisted files per tenant
tenant_files = {
"alpha": {"config.yaml", "meta.json"},
"beta": {"settings.yaml"},
}
class FileRequest(BaseModel):
filename: str
def resolve_safe_base(tenant_id: str) -> str:
# Map tenant identifier to a container-safe base directory
return f"/app/data/{tenant_id}"
@app.post("/files/")
async def read_file(request: FileRequest, api_key: str):
tenant_id = API_KEY_TO_TENANT.get(api_key)
if not tenant_id:
raise HTTPException(status_code=401, detail="Invalid API key")
if request.filename not in tenant_files.get(tenant_id, set()):
raise HTTPException(status_code=400, detail="File not allowed")
base = resolve_safe_base(tenant_id)
safe_path = os.path.realpath(os.path.join(base, request.filename))
# Ensure the resolved path stays within the tenant base
if not safe_path.startswith(os.path.realpath(base)):
raise HTTPException(status_code=403, detail="Path traversal prevented")
try:
with open(safe_path, "r") as f:
content = f.read()
except OSError:
raise HTTPException(status_code=404, detail="File unavailable")
return {"content": content}
Additional remediation practices:
- Do not construct shell commands from API key or filename inputs; use direct file operations or language-native libraries instead of subprocess calls.
- Drop unnecessary Linux capabilities from the container and run as a non-root user to limit the impact of any potential escape attempt.
- Apply strict schema validation for all inputs, including filename length, character set, and path components.
- If you use middleBrick’s CLI (
middlebrick scan <url>) or GitHub Action, you can enforce a minimum security score threshold in CI/CD to block deployments where such risky patterns are detected.