Time Of Check Time Of Use in Echo Go with Basic Auth
Time Of Check Time Of Use in Echo Go with Basic Auth — how this specific combination creates or exposes the vulnerability
Time Of Check Time Of Use (TOCTOU) occurs when the outcome of a security check is not tightly coupled to the subsequent action, allowing an attacker to change state between the check and the use. In Echo Go, combining Basic Auth with file or resource access can create TOCTOU when authorization logic validates credentials or permissions, then later opens or serves a resource without re-validating identity or state.
Consider a handler that first validates an HTTP Basic Auth token and then opens a user-provided filename. The check confirms the user is authenticated and authorized to access a file path, but the actual file open occurs after the check. If the attacker can swap the target file between the check and the open (e.g., via a symlink or race condition on a shared path), they can read or write files they should not access. This is a classic TOCTOU: the check and the use operate on different references, and the check does not prevent state change in the resource itself.
In Echo Go, this can also manifest when Basic Auth guards an endpoint that internally performs a secondary lookup, such as constructing a filesystem path from user input after authentication. The authentication check passes, but the derived path may be predictable or mutable, enabling path traversal or privilege escalation between the check and the use. Unlike frameworks with built-in middleware guards that tightly couple validation to routing, a manually implemented Basic Auth flow may leave a window where the subject of the check (e.g., a user identity) no longer maps to the object being accessed by the time the operation executes.
Real-world parallels include CVE-classic patterns where authenticated access leads to insecure direct object references (IDOR) when authorization is not rechecked at the point of data access. For example, an endpoint that verifies a token, fetches a document ID from the query, and then streams the file without confirming the document belongs to the authenticated user under the current session can expose data across concurrent requests. The scanner categories in middleBrick — such as Authentication, BOLA/IDOR, and Property Authorization — align with these risks because they detect whether authorization checks are applied consistently across the request lifecycle and whether outputs are validated before use.
middleBrick can surface these issues by correlating runtime behavior with OpenAPI/Swagger definitions and by testing unauthenticated and authenticated attack surfaces. While the scanner does not fix the logic, its findings include remediation guidance to tighten the window between check and use. For instance, ensuring that authorization is re-evaluated immediately before the resource operation, and that paths are derived from trusted, immutable sources, reduces the TOCTOU surface. In CI/CD, the middleBrick GitHub Action can fail builds if risk scores degrade, encouraging early detection of such patterns.
Basic Auth-Specific Remediation in Echo Go — concrete code fixes
To mitigate TOCTOU in Echo Go with Basic Auth, couple validation and resource access so that the identity used for the check is the same identity used for the operation, and avoid mutable references between the two. Prefer token-based or session-bound authorization after authentication, and ensure file or object paths are derived from trusted sources rather than user-controlled input without strict validation.
Below are two concrete approaches with syntactically correct Basic Auth examples for Echo Go.
Approach 1: Validate and use under the same authorization context
Instead of authenticating first and then looking up a resource by user input, embed the authorization check within the same logical step that accesses the resource. For file operations, resolve the absolute path before opening, and re-check ownership or permissions immediately before I/O. Do not rely on a prior authentication step alone to guarantee safety.
// Good pattern: resolve and verify within the same handler logic
func secureHandler(c echo.Context) error {
user, pass, ok := c.Request().BasicAuth()
if !ok {
return echo.ErrUnauthorized
}
// Validate credentials against a secure store
if !isValidUser(user, pass) {
return echo.ErrUnauthorized
}
// userID is mapped to an internal account, not derived directly from user input
userID := getUserIDForAuth(user)
// filename comes from user input but is strictly validated
requestedFile := c.QueryParam("file")
safePath, err := sanitizeAndResolve(userID, requestedFile)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid file request")
}
// Re-check permissions immediately before opening
if !hasAccess(userID, safePath) {
return echo.ErrForbidden
}
file, err := os.Open(safePath)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "unable to open file")
}
defer file.Close()
return c.SendStream(file, http.StatusOK)
}
func sanitizeAndResolve(userID int, input string) (string, error) {
base := fmt.Sprintf("/data/users/%d/", userID)
cleaned := filepath.Clean(input)
// Ensure the cleaned path does not escape the user base
if !strings.HasPrefix(cleaned, ".") && !strings.Contains(cleaned, "..") {
full := filepath.Join(base, cleaned)
// Ensure the resolved path remains within the user directory
if rel, err := filepath.Rel(base, full); err == nil && !strings.HasPrefix(rel, "..") {
return full, nil
}
}
return "", errors.New("invalid path")
}
Approach 2: Use short-lived tokens post-authentication to bind identity to resource access
After Basic Auth succeeds, issue a scoped token or session value that is required for subsequent resource operations. The token should encode the subject and any constraints, and be validated immediately before each sensitive use. This reduces the window where identity and resource mapping can diverge.
// Example: issue a scoped token after Basic Auth, then require it for file access
type ScopedToken struct {
UserID int
Scope string // e.g., "files:read"
Expiry time.Time
}
func authWithToken(c echo.Context) error {
user, pass, ok := c.Request().BasicAuth()
if !ok || !isValidUser(user, pass) {
return echo.ErrUnauthorized
}
userID := getUserIDForAuth(user)
token := &ScopedToken{
UserID: userID,
Scope: "files:read",
Expiry: time.Now().Add(5 * time.Minute),
}
// Encode token (e.g., JWT or opaque reference stored server-side)
tokenStr := encodeToken(token)
// Return token to client; client must present it for sensitive endpoints
return c.JSON(http.StatusOK, map[string]string{"token": tokenStr})
}
func fileHandler(c echo.Context) error {
tokenStr := c.Request().Header.Get("Authorization")
if tokenStr == "" {
return echo.ErrUnauthorized
}
token, err := decodeAndValidateToken(tokenStr)
if err != nil || token.Scope != "files:read" || time.Now().After(token.Expiry) {
return echo.ErrUnauthorized
}
requestedFile := c.QueryParam("file")
safePath, err := sanitizeAndResolve(token.UserID, requestedFile)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid file request")
}
if !hasAccess(token.UserID, safePath) {
return echo.ErrForbidden
}
file, err := os.Open(safePath)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "unable to open file")
}
defer file.Close()
return c.SendStream(file, http.StatusOK)
}
These patterns emphasize that authentication and authorization are not one-time gates but ongoing contextual checks. By revalidating identity and constraints immediately before resource use and by avoiding indirect references that can be swapped, you reduce the TOCTOU surface. middleBrick’s checks for Authentication, BOLA/IDOR, and Property Authorization help identify whether such gaps exist in an API’s behavior, and its remediation guidance supports tightening the check-to-use chain in Go services.