Time Of Check Time Of Use in Echo Go
How Time Of Check Time Of Use Manifests in Echo Go
Time Of Check Time Of Use (TOCTOU) vulnerabilities in Echo Go applications arise when the state of a resource changes between the moment it's checked and the moment it's used. In Echo Go's concurrent architecture, this race condition becomes particularly dangerous because multiple goroutines can access the same resource simultaneously.
The most common TOCTOU pattern in Echo Go occurs during file operations. Consider this vulnerable pattern:
func serveFile(c echo.Context) error {
path := c.Param("filepath")
// Time Of Check
if !fileExists(path) {
return echo.NewHTTPError(http.StatusNotFound, "File not found")
}
// Time Of Use - the file might have been deleted/changed
data, err := os.ReadFile(path)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Read error")
}
return c.JSON(http.StatusOK, data)
}
Between the fileExists check and os.ReadFile, an attacker could delete or replace the file, causing the application to serve different content than expected or crash.
Database operations in Echo Go are equally vulnerable. This pattern appears frequently in inventory management:
func purchaseItem(c echo.Context) error {
itemID := c.Param("id")
// Time Of Check
item, err := db.QueryItem(itemID)
if err != nil || item.Stock < 1 {
return echo.NewHTTPError(http.StatusNotFound, "Out of stock")
}
// Time Of Use - stock could change between queries
if err := db.UpdateStock(itemID, item.Stock-1); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Purchase failed")
}
return c.JSON(http.StatusOK, "Purchase successful")
}
Multiple concurrent requests could pass the stock check simultaneously, leading to overselling inventory.
Echo Go's middleware chain introduces additional TOCTOU opportunities. Authentication checks that happen early in the chain might become invalid by the time the handler executes:
func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Time Of Check
token := c.Request().Header.Get("Authorization")
if !validateToken(token) {
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid token")
}
// Token could expire before handler executes
return next(c)
}
}
Even Echo Go's context handling can introduce TOCTOU issues when values are set and later used:
func processRequest(c echo.Context) error {
// Time Of Check
userID := c.Get("user_id")
if userID == nil {
return echo.NewHTTPError(http.StatusUnauthorized, "User not authenticated")
}
// Time Of Use - context could be modified by middleware
return handleUserOperation(c, userID.(string))
}
These patterns are particularly problematic in Echo Go because of its lightweight, high-performance design that encourages concurrent processing without built-in synchronization mechanisms.
Echo Go-Specific Detection
Detecting TOCTOU vulnerabilities in Echo Go requires both static analysis and runtime monitoring. middleBrick's scanner specifically identifies these patterns in Echo Go applications through its black-box scanning approach.
middleBrick tests for TOCTOU by simulating concurrent access patterns. For file operations, it:
- Checks if file existence is verified before access
- Tests for race conditions by rapidly requesting the same resource
- Verifies proper error handling when files change state
- Scans for hardcoded file paths that might be manipulated
- Tests for directory traversal vulnerabilities that could bypass checks
For database operations, middleBrick's scanner:
- Identifies database queries that check state before modifying it
- Tests concurrent access to the same resource
- Verifies proper transaction handling
- Checks for missing database locks or atomic operations
Echo Go's middleware system requires special attention. middleBrick analyzes the middleware chain to identify:
- Authentication checks that don't persist state
- Authorization checks performed too early in the request lifecycle
- Context modifications that could invalidate earlier checks
The scanner also tests Echo Go's context handling patterns:
// middleBrick simulates concurrent context modification
func testContextTOCTOU(e *echo.Echo) {
e.GET("/test", func(c echo.Context) error {
// Simulate context modification between check and use
go func() {
time.Sleep(10 * time.Millisecond)
c.Set("user_id", "attacker")
}()
// Time Of Check
userID := c.Get("user_id")
if userID == nil {
return c.String(http.StatusOK, "Original user")
}
// Time Of Use - context could have changed
return c.String(http.StatusOK, fmt.Sprintf("User: %v", userID))
})
}
middleBrick's LLM security module also checks for TOCTOU in AI-specific endpoints, testing if system prompts or model configurations change between validation and execution.
Configuration analysis is another detection vector. middleBrick scans for:
- Hardcoded timeouts that could expire between checks
- Configuration values that might change during request processing
- Missing validation on dynamic configuration updates
The scanner provides Echo Go-specific remediation guidance, mapping findings to OWASP API Top 10 categories and providing severity scores based on the potential impact of the race condition.
Echo Go-Specific Remediation
Remediating TOCTOU vulnerabilities in Echo Go requires understanding Go's concurrency primitives and Echo's middleware architecture. The most effective approach is to eliminate the check-use gap entirely through atomic operations.
For file operations, use atomic file operations instead of separate check and use steps:
func serveFileAtomic(c echo.Context) error {
path := c.Param("filepath")
// Single atomic operation - no separate check needed
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return echo.NewHTTPError(http.StatusNotFound, "File not found")
}
return echo.NewHTTPError(http.StatusInternalServerError, "Read error")
}
return c.JSON(http.StatusOK, data)
}
Database operations require transactions to ensure atomicity:
func purchaseItemAtomic(c echo.Context) error {
itemID := c.Param("id")
// Use database transaction to ensure atomic check-modify
tx, err := db.Begin()
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Transaction error")
}
var item Item
err = tx.QueryRow("SELECT stock FROM items WHERE id = $1", itemID).Scan(&item.Stock)
if err != nil || item.Stock < 1 {
tx.Rollback()
return echo.NewHTTPError(http.StatusNotFound, "Out of stock")
}
_, err = tx.Exec("UPDATE items SET stock = stock - 1 WHERE id = $1", itemID)
if err != nil {
tx.Rollback()
return echo.NewHTTPError(http.StatusInternalServerError, "Purchase failed")
}
if err := tx.Commit(); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Commit failed")
}
return c.JSON(http.StatusOK, "Purchase successful")
}
Echo Go middleware can be made TOCTOU-resistant using context values that are set atomically:
func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
token := c.Request().Header.Get("Authorization")
claims, err := validateToken(token)
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid token")
}
// Set claims atomically in context
c.Set("auth_claims", claims)
return next(c)
}
}
func protectedHandler(c echo.Context) error {
// Single context access - no separate check needed
claims := c.Get("auth_claims")
if claims == nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Authentication required")
}
return c.JSON(http.StatusOK, claims)
}
For rate limiting in Echo Go, use atomic counters instead of separate check-increment operations:
import "sync/atomic"
var requestCounts = make(map[string]*uint64)
var requestCountsMutex sync.RWMutex
func rateLimitMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
clientIP := c.RealIP()
// Atomic increment and check
var count uint64
requestCountsMutex.RLock()
if counter, exists := requestCounts[clientIP]; exists {
count = atomic.AddUint64(counter, 1)
} else {
requestCountsMutex.RUnlock()
requestCountsMutex.Lock()
var newCounter uint64
count = atomic.AddUint64(&newCounter, 1)
requestCounts[clientIP] = &newCounter
requestCountsMutex.Unlock()
requestCountsMutex.RLock()
}
requestCountsMutex.RUnlock()
if count > 100 { // 100 requests limit
return echo.NewHTTPError(http.StatusTooManyRequests, "Rate limit exceeded")
}
return next(c)
}
}
Echo Go's built-in validation middleware should be used for input validation to ensure checks happen atomically with processing:
e.POST("/api/items", func(c echo.Context) error {
var item Item
if err := c.Bind(&item); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid input")
}
// Validation happens during binding - no separate check
if err := c.Validate(item); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return c.JSON(http.StatusCreated, item)
}, middleware.Validate())
Using Go's sync/atomic package for shared state access and Echo's context management features ensures that TOCTOU vulnerabilities are eliminated at the architectural level rather than patched individually.