CRITICAL command injectiongorilla muxjwt tokens

Command Injection in Gorilla Mux with Jwt Tokens

Command Injection in Gorilla Mux with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Command Injection occurs when an attacker can cause a program to execute arbitrary system commands. In a Go HTTP service using gorilla/mux for routing, this typically arises from insufficient input validation on route parameters or query values that are passed to a shell or an external command. JWT tokens are commonly used for authentication; they travel in the Authorization: Bearer <token> header and are parsed by middleware before the request reaches a handler. When JWT parsing or claims extraction is implemented insecurely, or when the token or its embedded claims are later used to construct shell commands, the combination can expose command injection.

Specifically, a vulnerability can appear in two linked scenarios. First, if the JWT validation logic or a custom claim (such as sub, username, or a custom field) is taken directly from the token and concatenated into a command string, an attacker who can influence the token (for example, via a compromised issuer, an algorithm confusion, or a token obtained through another vector) may inject shell metacharacters like ;, &, or |. Second, if route variables extracted by gorilla/mux (for example, {username} or {commandID}) are mixed with JWT claims to build a command, unsanitized input from both sources can combine to form malicious instructions. Consider a handler that builds a system command using both a URL path parameter and a JWT claim without proper escaping:

username := mux.Vars(r)["username"]
subject := claims["sub"].(string)
cmd := fmt.Sprintf("usermgr show %s --id %s", username, subject)
output, err := exec.Command("sh", "-c", cmd).Output()

An attacker who controls either the username route parameter or their own JWT’s sub claim can inject additional commands. For example, a username value of admin; cat /etc/passwd would cause the system to disclose sensitive file contents. Even when JWTs are properly verified, trusting any part of the token or its context to build shell commands is hazardous because tokens can be stolen or replayed, and claims can be forged if validation is weak.

In a gorilla/mux setup, the attack surface includes route parameters, query strings, and headers, in addition to JWT claims. If middleware places the token or its claims into a context that later reaches a command-building function, the handler may inadvertently trust that data. Common pitfalls include using sh -c with formatted strings, insufficient input sanitization, and overly permissive type assertions on claims. The risk is compounded when logging or error reporting echoes command snippets, potentially leaking tokens or sensitive data. Effective mitigation requires strict input validation, avoiding shell invocation, and using structured, language-native APIs instead of constructing shell commands.

Jwt Tokens-Specific Remediation in Gorilla Mux — concrete code fixes

To secure a gorilla/mux service that uses JWTs, avoid building shell commands from any combination of route parameters, query values, headers, or JWT claims. Instead, validate and sanitize all external inputs and prefer standard library functions that do not involve a shell. Below are concrete, secure patterns and code examples.

1. Validate and use JWT claims as structured data, not as command input

Parse and verify the JWT once in middleware, then place only the needed, validated claim values into the request context. Do not concatenate claims into shell commands.

func jwtMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tokenString := r.Header.Get("Authorization")
        if tokenString == "" {
            http.Error(w, "missing token", http.StatusUnauthorized)
            return
        }
        // Remove "Bearer " prefix if present
        tokenString = strings.TrimPrefix(tokenString, "Bearer ")
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            // TODO: use appropriate key and signing method validation
            return myPublicKey, nil
        })
        if err != nil || !token.Valid {
            http.Error(w, "invalid token", http.StatusUnauthorized)
            return
        }
        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok {
            http.Error(w, "invalid claims", http.StatusUnauthorized)
            return
        }
        // Store only safe, validated values in context
        ctx := context.WithValue(r.Context(), "uid", claims["sub"])
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

2. Avoid shell invocation; use exec.Command without a shell

Never use sh -c or cmd.Command("sh", "-c", ...) . Use exec.Command with explicit arguments so that arguments are passed directly to the binary without shell interpretation.

func showUserHandler(w http.ResponseWriter, r *http.Request) {
    username := mux.Vars(r)["username"]
    // Validate username format strictly (alphanumeric and limited length)
    if !regexp.MustCompile(`^[a-zA-Z0-9_-]{1,32}$`).MatchString(username) {
        http.Error(w, "invalid username", http.StatusBadRequest)
        return
    }
    uid := r.Context().Value("uid")
    if uid == nil {
        http.Error(w, "unauthorized", http.StatusUnauthorized)
        return
    }
    // Use exec.Command without shell; arguments are passed directly
    cmd := exec.Command("usermgr", "show", username, "--id", fmt.Sprintf("%v", uid))
    output, err := cmd.Output()
    if err != nil {
        http.Error(w, "command failed", http.StatusInternalServerError)
        return
    }
    w.Write(output)
}

3. Sanitize and strictly type-assert claims

Always perform strict type assertions and validation on JWT claims. Do not trust claim values to be strings or to be safe for shell use.

subject, ok := claims["sub"]
if !ok {
    http.Error(w, "missing subject", http.StatusBadRequest)
    return
}
sid, ok := subject.(string)
if !ok || sid == "" {
    http.Error(w, "invalid subject claim", http.StatusBadRequest)
    return
}

4. Use structured APIs instead of command construction

Where possible, replace command execution with native library calls or well-defined service APIs. This eliminates shell injection risk entirely and is more reliable than trying to sanitize input for shell use.

// Prefer this over building a shell command
err := usermgr.Show(username, subject)
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Can a JWT token itself introduce command injection if it is not properly validated?
Yes. If a server decodes a JWT without verifying the signature or algorithm and then uses claims (such as sub, username, or custom fields) to build shell commands, an attacker who can craft a token can inject shell metacharacters. Always validate tokens fully (signature, issuer, audience) and treat claims as untrusted input.
What are safer alternatives to using exec.Command with shell in a gorilla/mux API?
Use exec.Command with explicit arguments and no shell (e.g., exec.Command("usermgr", "show", username, "--id", id)). Better yet, use native Go libraries or gRPC/HTTP APIs for the functionality you need, avoiding shell invocation entirely.