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 detectedmiddleBrick 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 ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |