Path Traversal in Echo Go with Cockroachdb
Path Traversal in Echo Go with Cockroachdb — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when an API endpoint uses user-supplied input to construct filesystem or database paths without proper validation or sanitization. In an Echo Go service that uses Cockroachdb, this typically manifests through endpoints that accept identifiers, file names, or object keys and use them directly in SQL queries or row-level access patterns. Because Cockroachdb is a distributed SQL database, standard SQL constructs apply, but the way queries are built in Go can inadvertently expose directory traversal or unauthorized data access patterns.
Consider an endpoint that retrieves user documents by ID, where the ID is passed as a URL parameter and interpolated into a Cockroachdb query. If the developer does not validate that the ID conforms to an expected format (e.g., UUID or integer), an attacker can supply sequences like ../../../etc/passwd or encoded variants, attempting to traverse logical boundaries. While Cockroachdb does not expose a traditional filesystem, path traversal in this context refers to unauthorized access to rows outside the intended scope, such as other tenants or administrative records, especially when row-level security is not enforced or when the query uses concatenated strings rather than parameterized statements.
In Echo Go, a vulnerable route might look like this, where the id path parameter is used directly in a SQL string:
e.GET('/documents/:id', func(c echo.Context) error {
id := c.Param("id")
var doc Document
// Unsafe: string concatenation allows path traversal via crafted IDs
query := fmt.Sprintf("SELECT id, content, owner_id FROM documents WHERE id = '%s'", id)
if err := db.Get(c.Request().Context(), &doc, query); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return c.JSON(http.StatusOK, doc)
})
An attacker can supply an ID such as 123' OR '1'='1 or ../../../admin/config (if the backend interprets it as a logical path in application logic), potentially bypassing intended access controls. Because the query is built via string formatting, Cockroachdb receives a statement that may return multiple rows or data belonging to other users. This combination of Echo Go routing, string-based SQL construction, and Cockroachdb’s SQL execution amplifies the risk of Insecure Direct Object References (IDOR) and BOLA (Broken Level Authorization) when input is not strictly validated and queries are not parameterized.
Additionally, if the application layer performs path manipulations before database access — for example, constructing file system paths to store attachments and then storing metadata in Cockroachdb — an attacker could traverse directories using encoded sequences like ..%2F or null bytes (if legacy handling exists). Even though Cockroachdb stores data in a distributed key-value layer, the logical mapping between user-supplied keys and internal storage can be abused if the application does not canonicalize and validate paths before use.
Cockroachdb-Specific Remediation in Echo Go — concrete code fixes
Remediation focuses on strict input validation, parameterized queries, and enforcing ownership checks. With Cockroachdb, always use placeholders and avoid string interpolation for SQL statements. Below are concrete, safe patterns for Echo Go services interacting with Cockroachdb.
1. Use parameterized queries with placeholders
Instead of building SQL with fmt.Sprintf, use placeholders supported by the Cockroachdb Go driver. This ensures user input is treated strictly as data, not executable code or path segments.
e.GET('/documents/:id', func(c echo.Context) error {
id := c.Param("id")
// Validate format, e.g., UUID or positive integer
if !isValidDocumentID(id) {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid document identifier"})
}
var doc Document
// Safe: parameterized query with placeholders
const query = `SELECT id, content, owner_id FROM documents WHERE id = $1`
if err := db.Get(c.Request().Context(), &doc, query, id); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
// Enforce ownership or tenant context here
if doc.OwnerID != getCurrentUser(c) {
return c.JSON(http.StatusForbidden, map[string]string{"error": "access denied"})
}
return c.JSON(http.StatusOK, doc)
})
2. Validate and canonicalize paths before any filesystem or logical access
If your workflow involves file paths stored in Cockroachdb or passed to external storage, resolve and sanitize them using Go’s path/filepath package before using them in queries or filesystem operations. Never trust raw user input.
import "path/filepath"
func sanitizeAndStore(c echo.Context, filename string, userID string) error {
// Clean path to remove traversal sequences
cleanName := filepath.Clean(filename)
// Reject paths that attempt directory traversal after cleaning
if cleanName != filename || filepath.IsAbs(cleanName) {
return errors.New("invalid filename")
}
// Use parameterized SQL to associate file metadata with owner
const insertSQL = `INSERT INTO attachments (user_id, filename, created_at) VALUES ($1, $2, NOW())`
_, err := db.Exec(c.Request().Context(), insertSQL, userID, cleanName)
return err
}
3. Enforce row-level security and ownership checks
Even with parameterized queries, ensure that each request verifies ownership or tenant context. Cockroachdb does not enforce application-level authorization, so this must be implemented in Go. Extend your queries to include ownership filters.
const secureQuery = `SELECT id, content FROM documents WHERE id = $1 AND owner_id = $2`
err := db.Get(c.Request().Context(), &doc, secureQuery, id, currentUserID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return c.JSON(http.StatusNotFound, nil)
}
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
4. Reject suspicious input patterns proactively
Add middleware or validation helpers to detect common traversal patterns before they reach the database. This reduces load on Cockroachdb and prevents noisy logs.
func isValidDocumentID(id string) bool {
// Example: accept only UUIDv4 pattern
matched, _ := regexp.MatchString(`^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$`, id)
return matched
}
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |