HIGH sql injectionfiberjwt tokens

Sql Injection in Fiber with Jwt Tokens

Sql Injection in Fiber with Jwt Tokens — how this specific combination creates or exposes the vulnerability

SQL injection in a Fiber application that uses JWT tokens for authentication commonly occurs when developers trust the decoded claims without additional validation and concatenate them into SQL queries. A typical vulnerable pattern is extracting a user identifier from the JWT (e.g., subject or a custom claim) and using it directly in a string-based query. Because JWTs are often accepted as proof of identity, developers may assume the identifier is safe, but the value is still user-influenced from the token payload. If the application builds queries by interpolating or formatting this identifier, an attacker who can influence the token payload (via a weak secret, algorithm confusion, or a compromised signing key) can inject SQL.

Consider a login flow where a valid JWT is issued after authentication, and a subsequent handler uses the sub claim to fetch user details. If the handler does not treat the claim as untrusted, it might construct a query like SELECT * FROM users WHERE id = ' + userID. An attacker who can control the token (e.g., via a none algorithm or a leaked secret) could set sub to ' OR 1=1 --, leading to unauthorized data access. Even when tokens are verified, injection can still arise if the application uses the token’s user ID in parameterized contexts incorrectly, such as dynamic table or column names, where placeholders are not supported and concatenation is mistakenly used.

In a Fiber app, routes often rely on middleware to validate JWTs and attach claims to the context. If downstream handlers then build SQL using those claims without sanitization, the attack surface expands. For instance, an endpoint like /users/:id might accept an id from the URL while also trusting a userID from the JWT. If the handler merges these sources or uses the JWT claim to dynamically choose a database, improper handling can bypass intended access controls. Specific real-world attack patterns include leveraging SQL injection to bypass authentication checks, escalate privileges by modifying role claims, or exfiltrate data via UNION-based techniques. The combination of JWT-based authorization and dynamic SQL thus requires strict separation of trust boundaries: treat JWT claims as input, validate and sanitize them, and never interpolate them into queries.

Jwt Tokens-Specific Remediation in Fiber — concrete code fixes

To remediate SQL injection risks when using JWT tokens in Fiber, always treat token claims as untrusted input and enforce strict separation between authentication data and query construction. Use parameterized queries or prepared statements for any value derived from the JWT, and avoid dynamic SQL for table or column names. Below are concrete, secure code examples for common scenarios.

Secure handler with parameterized query (PostgreSQL using database/sql with pgx)

// Assume a middleware that validates JWT and sets userID in context
userID, ok := ctx.Locals("userID").(string)
if !ok {
    ctx.Status(fiber.StatusUnauthorized)
    return
}

// Use parameterized query with placeholders
var user User
err := db.QueryRow(context.Background(), "SELECT id, email, role FROM users WHERE id = $1", userID)
if err != nil {
    ctx.Status(fiber.StatusInternalServerError)
    return
}
if err := json.NewDecoder(resp.Body).Scan(&user); err != nil {
    ctx.Status(fiber.StatusInternalServerError)
    return
}
json.NewEncoder(resp.Body).Encode(user)

Dynamic table or column names: whitelist approach

When table or column names must be dynamic (which is rare), do not interpolate values from the JWT. Instead, use a strict allowlist and map safe identifiers.

allowedTables := map[string]string{
    "profile": "users",
    "settings": "user_settings",
}

table, ok := allowedTables[userSuppliedTable]
if !ok {
    ctx.Status(fiber.StatusBadRequest)
    return
}

// Use fmt.Sprintf only for identifiers after strict validation, or better, avoid dynamic SQL entirely.
query := fmt.Sprintf("SELECT email FROM %s WHERE id = $1", table)
var email string
err := db.QueryRow(context.Background(), query, userID).Scan(&email)

JWT validation middleware example with secure claims handling

func JWTMiddleware() fiber.Handler {
    return func(c *fiber.Ctx) error {
        auth := c.Get("Authorization")
        if auth == "" {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Authorization header required"})
        }

        const bearerPrefix = "Bearer "
        if !strings.HasPrefix(auth, bearerPrefix) {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid authorization format"})
        }

        tokenString := strings.TrimPrefix(auth, bearerPrefix)
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
            }
            return []byte(os.Getenv("JWT_SECRET")), nil
        })

        if err != nil || !token.Valid {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid token"})
        }

        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid token claims"})
        }

        userID, ok := claims["sub"].(string)
        if !ok || userID == "" {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "missing sub claim"})
        }

        // Validate additional claims as needed (iss, aud, exp handled by library)
        c.Locals("userID", userID)
        return c.Next()
    }
}

Additional secure practices

  • Use well-audited JWT libraries and verify the algorithm explicitly to prevent algorithm confusion attacks.
  • Validate standard claims such as iss, aud, and exp to reduce the risk of accepting tokens intended for another service.
  • Apply the principle of least privilege: scope token claims to the minimum required for the operation, and enforce authorization checks server-side.
  • Log suspicious inputs and failed validation attempts for monitoring, but ensure logs do not include raw query strings with sensitive data.

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's 'sub' claim still be used safely in SQL queries if the token is properly signed?
Yes, but only if you treat the claim as untrusted input and use parameterized queries. Signature verification confirms the token was issued by your authority, but it does not prevent malicious values; always use placeholders (e.g., $1) and never interpolate the claim into the SQL string.
What should I do if I need dynamic table or column names in Fiber when working with JWT-derived input?
Avoid dynamic SQL for identifiers when possible. If unavoidable, use a strict allowlist mapping safe keys to actual names, and never directly interpolate JWT claims. Construct the query with validated identifiers only, and continue to use parameterized queries for all values.