HIGH path traversalaxumcockroachdb

Path Traversal in Axum with Cockroachdb

Path Traversal in Axum with Cockroachdb — how this specific combination creates or exposes the vulnerability

Path Traversal occurs when an API accepts user-controlled input used to construct file paths or database identifiers without adequate validation, allowing an attacker to access files or data outside the intended directory or scope. In an Axum application using Cockroachdb, this typically manifests through endpoints that accept identifiers (e.g., user_id, document_id) and use them in SQL queries without proper authorization checks or input sanitization.

Consider an endpoint that retrieves a user document by ID:

// WARNING: This pattern is vulnerable to IDOR and path traversal via crafted IDs
async fn get_document(
    Path(doc_id): Path<String>,
    pool: &PgPool,
) -> Result<Json<Document>, (StatusCode, String)> {
    let row = sqlx::query!("SELECT id, content FROM documents WHERE id = $1", doc_id)
        .fetch_optional(pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    match row {
        Some(r) => Ok(Json(Document { id: r.id, content: r.content })),
        None => Err((StatusCode::NOT_FOUND, "Not found".to_string())),
    }
}

If doc_id is not constrained to the authenticated user’s scope (e.g., missing tenant or ownership checks), an attacker can traverse paths in the logical data space by iterating through IDs. Although Cockroachdb does not expose a traditional filesystem path, the database identifier space becomes the traversal target. Combined with BOLA (Broken Level Access) weaknesses, this allows unauthorized access across what appears to be isolated records.

middleBrick detects this pattern during its unauthenticated scan by observing whether endpoints that accept user-supplied identifiers enforce authorization relative to a subject (e.g., via JWT subject claims or session context). The scanner cross-references OpenAPI/Swagger definitions with runtime behavior to identify missing authorization constraints on parameters used in SQL statements. Findings often map to the OWASP API Top 10 Broken Object Level Authorization and can intersect with Data Exposure checks when sensitive Cockroachdb rows are accessible without proper scoping.

Additional risk arises when endpoints expose filesystem-like operations (e.g., serving uploaded files) and use user input to build paths without canonicalization. Even when Cockroachdb stores metadata only, Axum handlers that concatenate user input into file paths on the server before passing to Cockroachdb for lookup can enable directory traversal via ../../ sequences, leading to Data Exposure or server-side request forgery when combined with SSRF probes.

Cockroachdb-Specific Remediation in Axum — concrete code fixes

Remediation focuses on strict input validation, parameterized queries, and enforcing ownership or tenant context at the application layer. Avoid string concatenation for SQL; use strongly typed queries with explicit bind variables. Ensure every query that references a row ID also validates that the row belongs to the requesting subject.

1. Enforce ownership checks using the authenticated subject:

async fn get_document_secure(
    Path(doc_id): Path<String>,
    Extension(pool): Extension<PgPool>,
    // Assume extract_subject returns a validated subject claim
    subject: Subject, 
) -> Result<Json<Document>, (StatusCode, String)> {
    let row = sqlx::query!((
        "SELECT id, content, owner_id FROM documents WHERE id = $1 AND owner_id = $2"
    ), doc_id, subject.id)
    .fetch_optional(&pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    match row {
        Some(r) => Ok(Json(Document { id: r.id, content: r.content })),
        None => Err((StatusCode::FORBIDDEN, "Access denied".to_string())),
    }
}

This ensures that even if an attacker guesses or iterates IDs, they cannot access records where owner_id does not match the authenticated subject. The query uses Cockroachdb’s parameterized SQL to prevent injection and relies on the database to enforce row-level filtering.

2. Validate and restrict identifier formats before using them in queries:

use validator::Validate;

#[derive(Validate)]
struct DocumentPath {
    #[validate(length(min = 1), regex("^[a-zA-Z0-9_-]+$"))]
    doc_id: String,
}

async fn get_document_validated(
    Path(path): Path<DocumentPath>,
    pool: &PgPool,
    subject: Subject,
) -> Result<Json<Document>, (StatusCode, String)> {
    let row = sqlx::query!((
        "SELECT id, content FROM documents WHERE id = $1 AND owner_id = $2"
    ), path.doc_id, subject.id)
    .fetch_optional(pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    match row {
        Some(r) => Ok(Json(Document { id: r.id, content: r.content })),
        None => Err((StatusCode::FORBIDDEN, "Access denied".to_string())),
    }
}

By constraining doc_id with a regex that allows only safe characters, you reduce the risk of injection or misuse. Combined with parameterized queries, this approach aligns with secure coding practices for database interactions in Rust-based Axum services.

3. For file-serving endpoints, sanitize paths and avoid user-controlled directory traversal:

use std::path::{Path, PathBuf};

fn sanitize_path(base: &Path, user_path: &str) -> Option<PathBuf> {
    let normalized = PathBuf::from(user_path).strip_prefix(".").ok()?;
    let full = base.join(normalized).canonicalize().ok()?;
    if full.starts_with(base) {
        Some(full)
    } else {
        None
    }
}

async fn serve_file(
    Path(filename): Path<String>,
    pool: &PgPool,
) -> Result<NamedFile, (StatusCode, String)> {
    let base = Path::new("/safe/uploads");
    let safe_path = sanitize_path(base, &filename)
        .ok_or_else(|| (StatusCode::BAD_REQUEST, "Invalid path".to_string()))?;
    // Use safe_path to verify file exists in allowed directory before serving
    NamedFile::open(safe_path).map_err(|_| (StatusCode::NOT_FOUND, "File not found".to_string()))
}

This pattern ensures that user input cannot escape the intended base directory, mitigating traversal attempts before any database interaction is required.

middleBrick’s CLI can validate that such patterns are present in your codebase and that endpoints are scanned for insecure parameter usage. Use the GitHub Action to fail builds if insecure path-building patterns are detected, and leverage the MCP Server to get inline guidance while editing Axum handlers in your IDE.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Can Path Traversal in Axum with Cockroachdb lead to unauthorized data access even if the database enforces row-level security?
Yes. If application-layer ownership checks are missing, an attacker can traverse logical paths by manipulating identifiers, bypassing UI or API expectations. Cockroachdb row-level security can complement defenses but should not replace explicit authorization checks in Axum handlers.
Does using UUIDs instead of sequential IDs prevent Path Traversal risks in Axum services backed by Cockroachdb?
UUIDs reduce predictability but do not prevent traversal if authorization is missing. An endpoint that returns any document when supplied a valid UUID but lacks tenant or subject checks remains vulnerable to IDOR and data exposure; always enforce ownership alongside parameterized queries.