HIGH missing authenticationecho go

Missing Authentication in Echo Go

How Missing Authentication Manifests in Echo Go

Missing authentication in Echo Go applications often appears in the most critical endpoints where developers assume the API is "internal" or will be protected by network controls. The Echo Go framework, built on top of Go's net/http package, provides middleware for authentication but requires explicit registration. A common manifestation is when developers create handler functions without wrapping them in authentication middleware, leaving endpoints completely exposed.

For example, consider an admin dashboard endpoint that should only be accessible to authenticated users:

e := echo.New()
// Missing authentication middleware!
e.GET("/admin/dashboard", adminDashboardHandler)

This endpoint is now accessible to anyone who discovers it. The vulnerability becomes more severe when combined with Echo Go's parameter binding, which automatically parses request data into structs:

type AdminConfig struct {
    MaxUsers int `json:"max_users"`
    BillingEnabled bool `json:"billing_enabled"`
}

func adminDashboardHandler(c echo.Context) error {
    var config AdminConfig
    if err := c.Bind(&config); err != nil {
        return err
    }
    // No authentication check
    return c.JSON(http.StatusOK, config)
}

Without authentication, an attacker can modify max_users or billing_enabled parameters to alter system behavior. Echo Go's default error handling can also leak information when authentication is missing:

e.GET("/internal/health", healthHandler)

func healthHandler(c echo.Context) error {
    // Exposes internal service status
    return c.JSON(http.StatusOK, map[string]string{
        "status": "healthy",
        "db_connection": "connected",
        "redis_connection": "connected",
    })
}

Attackers can use this information for targeted attacks. Another common pattern is missing authentication in WebSocket endpoints:

e.GET("/ws", func(c echo.Context) error {
    ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
    if err != nil {
        return err
    }
    defer ws.Close()
    
    for {
        // No authentication before establishing WebSocket
        msgType, msg, err := ws.ReadMessage()
        // Process unauthenticated messages
    }
})

Echo Go's JWT middleware provides a straightforward solution, but it must be applied correctly:

e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
    SigningKey: []byte("secret"),
    
    // Critical: specify which endpoints need auth
    Skipper: func(c echo.Context) bool {
        // Only skip auth for explicitly public endpoints
        if c.Path() == "/public" || c.Path() == "/health" {
            return true
        }
        return false
    },
}))

Echo Go-Specific Detection

Detecting missing authentication in Echo Go applications requires both static analysis and runtime scanning. Static analysis can identify patterns where authentication middleware is not applied to handler functions. Using Go's reflection package, you can scan your Echo Go application for unprotected endpoints:

func findUnprotectedEndpoints(e *echo.Echo) []string {
    var unprotected []string
    
    // Check if JWT middleware is registered
    hasAuth := false
    for _, m := range e.Pre() {
        if _, ok := m.(*middleware.JWTConfig); ok {
            hasAuth = true
        }
    }
    
    if !hasAuth {
        // If no auth middleware, all endpoints are unprotected
        for _, r := range e.Routes() {
            unprotected = append(unprotected, r.Path)
        }
        return unprotected
    }
    
    // More sophisticated analysis would check Skipper functions
    // and identify endpoints that bypass authentication
    return unprotected
}

Runtime scanning with middleBrick provides comprehensive detection by actively testing endpoints for authentication bypasses. middleBrick's black-box scanner sends requests to Echo Go endpoints and analyzes responses for authentication failures:

# Scan an Echo Go API endpoint
middlebrick scan https://api.example.com/v1/users

# Output includes authentication checks:
# - Authentication Bypass: GET /admin/dashboard (Status: 200)
# - Missing Auth on /internal/health (Status: 200)
# - Unauthenticated WebSocket access detected

middleBrick specifically tests Echo Go's common authentication patterns by attempting to access endpoints without credentials and analyzing response codes. For Echo Go applications using JWT tokens, middleBrick attempts requests with invalid tokens, expired tokens, and no tokens to verify authentication enforcement.

Echo Go's middleware stack makes detection straightforward because middleware executes in order. A missing authentication middleware in the chain indicates unprotected endpoints:

func checkMiddlewareStack(e *echo.Echo) error {
    for _, route := range e.Routes() {
        // Simulate request to check if auth middleware is in stack
        c := e.NewContext(
            httptest.NewRequest("GET", route.Path, nil),
            httptest.NewRecorder(),
        )
        
        // Check if request passes through auth middleware
        passedAuth := false
        for _, m := range e.Pre() {
            if _, ok := m.(*middleware.JWTConfig); ok {
                passedAuth = true
                break
            }
        }
        
        if !passedAuth && !isPublicEndpoint(route.Path) {
            return fmt.Errorf("endpoint %s missing authentication", route.Path)
        }
    }
    return nil
}

Echo Go-Specific Remediation

Remediating missing authentication in Echo Go requires implementing proper authentication middleware and ensuring all sensitive endpoints are protected. The most straightforward approach uses Echo Go's built-in JWT middleware:

e := echo.New()

// Configure JWT middleware with proper signing key
jwtConfig := middleware.JWTWithConfig(middleware.JWTConfig{
    SigningKey: []byte(os.Getenv("JWT_SECRET")),
    
    // Only skip auth for explicitly public endpoints
    Skipper: func(c echo.Context) bool {
        path := c.Path()
        
        // Public endpoints
        publicPaths := map[string]bool{
            "/public": true,
            "/health": true,
            "/login": true,
            "/register": true,
        }
        
        return publicPaths[path]
    },
})

e.Use(jwtConfig)

For Echo Go applications requiring role-based access control (RBAC), extend the JWT middleware with custom claims:

type Role string

const (
    RoleAdmin Role = "admin"
    RoleUser  Role = "user"
)

type CustomClaims struct {
    jwt.StandardClaims
    Role Role `json:"role"`
}

// Admin-only middleware
func isAdmin(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        user := c.Get("user").(*jwt.Token)
        claims := user.Claims.(*CustomClaims)
        
        if claims.Role != RoleAdmin {
            return echo.NewHTTPError(http.StatusForbidden, "Admin access required")
        }
        
        return next(c)
    }
}

// Apply to specific routes
e.GET("/admin/dashboard", adminDashboardHandler, isAdmin)

Echo Go's context binding makes it easy to extract user information in handlers:

func profileHandler(c echo.Context) error {
    user := c.Get("user").(*jwt.Token)
    claims := user.Claims.(*CustomClaims)
    
    // Fetch user-specific data
    userID := claims.ID
    userProfile, err := getUserProfile(userID)
    if err != nil {
        return echo.NewHTTPError(http.StatusInternalServerError, "Profile not found")
    }
    
    return c.JSON(http.StatusOK, userProfile)
}

For WebSocket endpoints in Echo Go, authentication must occur before upgrading the connection:

e.GET("/ws", func(c echo.Context) error {
    // Verify JWT before WebSocket upgrade
    authHeader := c.Request().Header.Get("Authorization")
    if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
        return echo.NewHTTPError(http.StatusUnauthorized, "Missing token")
    }
    
    token := strings.TrimPrefix(authHeader, "Bearer ")
    parsedToken, err := jwt.ParseWithClaims(token, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
        return []byte(os.Getenv("JWT_SECRET")), nil
    })
    
    if err != nil || !parsedToken.Valid {
        return echo.NewHTTPError(http.StatusUnauthorized, "Invalid token")
    }
    
    // Upgrade to WebSocket only after successful auth
    ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
    if err != nil {
        return err
    }
    defer ws.Close()
    
    // Process authenticated WebSocket messages
    for {
        msgType, msg, err := ws.ReadMessage()
        if err != nil {
            break
        }
        // Handle authenticated WebSocket communication
    }
    
    return nil
})

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

How can I test if my Echo Go API has missing authentication?
Use middleBrick's self-service scanner to test your Echo Go API endpoints. middleBrick actively attempts to access endpoints without authentication credentials and analyzes response codes and behaviors. For manual testing, try accessing sensitive endpoints without JWT tokens or API keys - if you receive successful responses (200 OK) instead of authentication errors (401/403), authentication is missing. middleBrick also checks for Echo Go-specific patterns like unprotected WebSocket endpoints and parameter binding vulnerabilities.
What's the difference between Echo Go's JWT middleware and basic auth middleware?
Echo Go's JWT middleware provides stateless authentication using JSON Web Tokens, which is ideal for REST APIs and microservices. It verifies token signatures and extracts claims for authorization decisions. Basic auth middleware, on the other hand, uses username/password credentials sent with each request (encoded in base64). JWT is generally more secure for modern APIs because tokens can contain user roles and permissions, support expiration, and don't require server-side session storage. For Echo Go applications, JWT is the recommended approach for most use cases.