Nosql Injection in Echo Go
How Nosql Injection Manifests in Echo Go
Nosql Injection in Echo Go applications typically occurs when user input is directly incorporated into MongoDB queries without proper sanitization. Echo Go's minimalist design makes it easy to accidentally create vulnerable endpoints that accept JSON payloads and use them to construct database queries.
The most common pattern involves Echo handlers that accept request bodies and pass them directly to MongoDB methods. Consider this vulnerable Echo Go endpoint:
func getUser(c echo.Context) error {
// Vulnerable: directly uses request body as query filter
var filter bson.M
if err := json.NewDecoder(c.Request().Body).Decode(&filter); err != nil {
return err
}
collection := mongoClient.Database("test").Collection("users")
var result User
// Attacker controls the entire filter document
err := collection.FindOne(context.Background(), filter).Decode(&result)
if err != nil {
return err
}
return c.JSON(http.StatusOK, result)
}An attacker could send:
{
"$where": "this.isAdmin === true",
"username": { "$ne": null }
}This bypasses authentication by injecting a JavaScript filter that returns all admin users. The vulnerability stems from Echo Go's flexible JSON binding—it accepts any structure and passes it directly to the database layer.
Another Echo Go-specific pattern involves using c.Bind() with struct tags that map directly to database fields:
type UserFilter struct {
Username string `json:"username"`
Role string `json:"role"`
}
func searchUsers(c echo.Context) error {
var filter UserFilter
if err := c.Bind(&filter); err != nil {
return err
}
// Even with struct binding, field values can contain injection
collection := mongoClient.Database("test").Collection("users")
query := bson.M{"role": filter.Role}
// If filter.Role = "admin", {$ne: null}", this creates:
// {"role": {"$ne": null, "$ne": "admin"}}
cursor, err := collection.Find(context.Background(), query)
if err != nil {
return err
}
return c.JSON(http.StatusOK, cursor)
}The issue becomes more severe when Echo Go handlers accept nested JSON objects that map to complex MongoDB queries, allowing attackers to construct arbitrary query operators like $or, $and, $expr, and $where clauses.
Echo Go-Specific Detection
Detecting Nosql Injection in Echo Go applications requires examining both the code structure and runtime behavior. middleBrick's scanner identifies Echo Go-specific patterns through several mechanisms:
Static Analysis: The scanner examines Echo Go route handlers for dangerous patterns like direct JSON decoding into BSON objects, unvalidated c.Bind() calls, and MongoDB operations that accept dynamic query parameters. It specifically looks for:
- echo.Context.Bind() calls that populate structs used in database queries
- json.Decoder usage that creates BSON documents from request bodies
- MongoDB Find(), FindOne(), UpdateOne(), DeleteOne() calls with dynamic parameters
- Echo Go's default JSON binding without validation middleware
- Missing input sanitization before database operations
Dynamic Testing: middleBrick actively probes Echo Go endpoints with Nosql Injection payloads. For a typical Echo Go API at https://api.example.com/v1/users, it sends test requests containing:
POST /v1/users/search
Content-Type: application/json
{
"$where": "sleep(1000) && this.username == 'test'",
"username": {"$ne": null}
}The scanner measures response timing to detect JavaScript execution and analyzes response structures for data leakage. It also tests for common MongoDB injection patterns like:
- $ne with null to bypass filters
- $or with always-true conditions
- $where with JavaScript code execution
- $regex with wildcard patterns
- ObjectId injection in URL parameters
Echo Go-Specific Checks: middleBrick recognizes Echo Go's conventions and tests accordingly:
- Endpoints using echo.Context.JSON() for responses
- Handlers with echo.Context.Param() for URL parameter extraction
- Echo Go middleware chains that might skip validation
- Common Echo Go project structures and naming conventions
The scanner generates a security score with specific findings for each vulnerable endpoint, showing the exact injection payload that succeeded and the potential impact (data exposure, authentication bypass, denial of service).
Echo Go-Specific Remediation
Securing Echo Go applications against Nosql Injection requires both architectural changes and input validation. The most effective approach combines strict typing with parameterized queries.
1. Use Struct Binding with Validation:
type UserSearchParams struct {
Username string `json:"username" validate:"omitempty,alphanum,max=50"`
Role string `json:"role" validate:"omitempty,oneof=admin user guest"`
Active *bool `json:"active" validate:"omitempty"`
}
func searchUsers(c echo.Context) error {
var params UserSearchParams
if err := c.Bind(¶ms); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid input"})
}
// Validate using go-playground/validator
if err := c.Validate(¶ms); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid parameters"})
}
// Build query safely with known fields
query := bson.M{}
if params.Username != "" {
query["username"] = params.Username
}
if params.Role != "" {
query["role"] = params.Role
}
if params.Active != nil {
query["active"] = *params.Active
}
collection := mongoClient.Database("test").Collection("users")
cursor, err := collection.Find(context.Background(), query)
if err != nil {
return err
}
return c.JSON(http.StatusOK, cursor)
}2. Implement Echo Go Middleware for Input Sanitization:
func nosqlInjectionMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Sanitize query parameters
for key, values := range c.QueryParams() {
for i, val := range values {
sanitized := sanitizeInput(val)
c.QueryParams()[key][i] = sanitized
}
}
// Sanitize JSON body for POST/PUT/PATCH
if c.Request().Body != nil {
body, err := io.ReadAll(c.Request().Body)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid body"})
}
// Simple sanitization - remove suspicious operators
sanitizedBody := removeMongoOperators(string(body))
c.Request().Body = io.NopCloser(bytes.NewBufferString(sanitizedBody))
}
return next(c)
}
}
// Register middleware globally
echo := echo.New()
echo.Use(nosqlInjectionMiddleware)3. Use Echo Go's Built-in Validation with Custom Validators:
type SafeString struct {
Value string `json:"value"`
}
func (s *SafeString) Sanitize() error {
// Reject MongoDB operators
if strings.Contains(s.Value, "$") {
return errors.New("MongoDB operators not allowed")
}
// Reject JavaScript code
if strings.Contains(s.Value, "function") || strings.Contains(s.Value, "return") {
return errors.New("JavaScript code not allowed")
}
return nil
}
func getUser(c echo.Context) error {
var params struct {
Username SafeString `json:"username" validate:"required"`
}
if err := c.Bind(¶ms); err != nil {
return err
}
if err := params.Username.Sanitize(); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
collection := mongoClient.Database("test").Collection("users")
query := bson.M{"username": params.Username.Value}
var result User
err := collection.FindOne(context.Background(), query).Decode(&result)
if err != nil {
return err
}
return c.JSON(http.StatusOK, result)
}4. Use Echo Go's Error Handling for Security:
func secureHandler(c echo.Context) error {
// Don't reveal database errors to clients
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered in secureHandler: %v", r)
c.JSON(http.StatusInternalServerError, map[string]string{"error": "Internal server error"})
}
}()
// Validate all inputs before use
username := c.QueryParam("username")
if !isValidUsername(username) {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid username format"})
}
// Use context timeouts for database operations
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
collection := mongoClient.Database("test").Collection("users")
query := bson.M{"username": username}
var result User
err := collection.FindOne(ctx, query).Decode(&result)
if err != nil {
if err == mongo.ErrNoDocuments {
return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
}
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Database error"})
}
return c.JSON(http.StatusOK, result)
}These remediation strategies leverage Echo Go's strengths—its middleware system, validation support, and clean handler patterns—while adding security layers that prevent Nosql Injection attacks.