Nosql Injection in Echo Go with Hmac Signatures
Nosql Injection in Echo Go with Hmac Signatures — how this specific combination creates or exposes the vulnerability
In an Echo Go service that uses Hmac Signatures for request authentication, developers often focus on ensuring the signature matches and that the signing key is protected. However, if user-controlled data is used to build NoSQL queries without proper validation or parameterization, the application can remain vulnerable to NoSQL Injection even when requests are authenticated with Hmac Signatures.
NoSQL databases such as MongoDB use query selectors and operator-based syntax (e.g., $gt, $in, $regex). If an Echo Go handler directly interpolates JSON or form values into these selectors, an attacker can inject operators or nested structures. For example, a username field supplied as {"$gt": ""} can change query semantics, bypassing intended filters. Authentication via Hmac Signatures ensures the request originates from a trusted client, but it does not sanitize or constrain the content of the payload. Therefore, trust in the signature must not extend to trusting the data itself.
This combination becomes particularly risky when signature verification occurs before input validation. An attacker can send a well-signed request with maliciously crafted JSON that modifies query logic. Common patterns in Echo Go include binding request bodies to map[string]interface{} or custom structs and then passing those values to a MongoDB collection. If the application dynamically constructs query filters using these bound values, NoSQL Injection can occur. Real-world attack patterns include authentication bypass via injected operators that always evaluate to true, data exfiltration by altering $or conditions, and privilege escalation by modifying role arrays.
Additionally, if the service stores or indexes user-controlled fields without normalization, regex-based operators like $regex can lead to ReDoS or unintended data exposure. Because Hmac Signatures do not constrain the schema or types of incoming data, the security boundary relies entirely on server-side validation. Without strict allowlists, type checks, and schema validation, the signed endpoint remains exposed. MiddleBrick detects this risk by correlating OpenAPI/Swagger definitions with runtime behavior, highlighting cases where authenticated endpoints accept untrusted input used in NoSQL queries.
Hmac Signatures-Specific Remediation in Echo Go — concrete code fixes
Remediation centers on strict input validation, schema enforcement, and safe query construction. Never directly pass user-supplied JSON into NoSQL selectors. Instead, use explicit structs with validated fields and build query filters programmatically. Below are concrete examples for Echo Go with Hmac Signatures.
Example 1: Signed request with safe struct binding and parameterized query
// Request payload struct with strict types
type UserQuery struct {
Username string `json:"username" validate:"alphanum,min=1,max=64"`
Role string `json:"role" validate:"oneof=admin,user,guest"`
}
// Handler with Hmac signature verification followed by validation
func getUser(c echo.Context) error {
// Assume VerifyHmacSignature returns error if invalid
if err := VerifyHmacSignature(c); err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "invalid signature") // nosec
}
// Bind and validate input
req := new(UserQuery)
if err := c.Bind(req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid request") // nosec
}
if cerr := c.Validate(req); cerr != nil {
return echo.NewHTTPError(http.StatusBadRequest, cerr.Error()) // nosec
}
// Safe query construction: no operator injection possible
filter := bson.M{
"username": req.Username,
"role": req.Role,
}
var result User
if err := collection.FindOne(c.Request().Context(), filter).Decode(&result); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "search failed") // nosec
}
return c.JSON(http.StatusOK, result)
}
// Simplified Hmac verification (implementation-specific)
func VerifyHmacSignature(c echo.Context) error {
// Extract signature from header
sig := c.Request().Header.Get("X-Signature")
if sig == "" {
return errors.New("missing signature")
}
// Compute expected signature using shared secret and canonical request body
body, _ := io.ReadAll(c.Request().Body)
c.Request().Body = io.NopCloser(bytes.NewBuffer(body))
expected := computeHmac(body, []byte(os.Getenv("HMAC_SECRET")))
if !hmac.Equal([]byte(sig), []byte(expected)) {
return errors.New("signature mismatch")
}
return nil
}
func computeHmac(body []byte, key []byte) string {
mac := hmac.New(sha256.New, key)
mac.Write(body)
return hex.EncodeToString(mac.Sum(nil))
}
Example 2: Defensive handling of dynamic filters with allowlists
// Safe dynamic filter builder
type FilterSpec struct {
Field string // validated against allowlist
Operator string // $eq, $in, $gt — allowlisted
Value any
}
func buildFilter(specs []FilterSpec) (bson.M, error) {
allowedFields := map[string]bool{"username": true, "role": true, "status": true}
allowedOps := map[string]bool{"$eq": true, "$in": true, "$gt": true}
filter := bson.M{}
for _, s := range specs {
if !allowedFields[s.Field] {
return nil, errors.New("invalid field")
}
if !allowedOps[s.Operator] {
return nil, errors.New("invalid operator")
}
filter[s.Field] = bson.M{s.Operator: s.Value}
}
return filter, nil
}
// Usage in handler after signature validation
func searchUsers(c echo.Context) error {
if err := VerifyHmacSignature(c); err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "invalid signature")
}
var raw []map[string]any
if err := c.Bind(&raw); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid json")
}
var specs []FilterSpec
for _, item := range raw {
f, ok := item["field"].(string)
o, ok2 := item["operator"].(string)
v, ok3 := item["value"]
if !ok || !ok2 || !ok3 {
return echo.NewHTTPError(http.StatusBadRequest, "malformed filter")
}
specs = append(specs, FilterSpec{Field: f, Operator: o, Value: v})
}
filter, err := buildFilter(specs)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
cursor, err := collection.Find(c.Request().Context(), filter)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "search failed")
}
defer cursor.Close(c.Request().Context())
var results []User
if err = cursor.All(c.Request().Context(), &results); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "decoding failed")
}
return c.JSON(http.StatusOK, results)
}
These patterns ensure that Hmac Signatures provide authentication without bypassing input validation. By binding to strict structs and using allowlists for field names and operators, you prevent operator injection while maintaining the integrity of authenticated requests.