Prototype Pollution in Echo Go
How Prototype Pollution Manifests in Echo Go
Prototype Pollution in Echo Go typically occurs through the framework's request binding mechanism, which uses Go's map[string]interface{} structures to bind JSON request bodies to struct fields. When Echo Go's binding unmarshaler processes JSON input, it can inadvertently modify the prototype chain of Go objects if not properly configured.
The most common attack vector involves sending JSON payloads with $__proto__ keys that Echo Go's binding system processes. Consider this vulnerable Echo Go handler:
func vulnerableHandler(c echo.Context) error {
var input map[string]interface{}
if err := c.Bind(&input); err != nil {
return err
}
// Prototype pollution can occur here
return c.JSON(http.StatusOK, input)
}
The issue arises because Echo Go's default JSON binding unmarshaler uses Go's standard encoding/json package, which doesn't inherently protect against prototype pollution patterns. An attacker could send:
{
"$__proto__": {
"isAdmin": true
}
}
In Go, this can lead to unexpected behavior when the input map is used to populate struct fields or when the data flows through reflection-based operations. The pollution becomes particularly dangerous when Echo Go handlers use the input map to construct responses or when the data is passed to template engines.
Another Echo Go-specific manifestation occurs with query parameter binding. Echo Go's c.QueryParams() returns a map[string][]string that can be manipulated through crafted query strings containing __proto__ keys, especially when these parameters are later used in struct binding operations.
Echo Go-Specific Detection
Detecting Prototype Pollution in Echo Go applications requires a multi-layered approach. The most effective method is using middleBrick's API security scanner, which specifically tests for prototype pollution vulnerabilities in Go applications.
middleBrick's detection process for Echo Go applications includes:
- Scanning for endpoints that accept JSON input without proper validation
- Testing for prototype pollution patterns using
$__proto__,constructor, andprototypekeys - Analyzing the application's response to determine if pollution succeeded
- Checking for unsafe reflection usage patterns in the codebase
To scan an Echo Go API with middleBrick:
npx middlebrick scan https://your-echo-go-api.com/api/v1/resource
The scanner tests multiple prototype pollution patterns specifically designed for Go applications, including variations that exploit Go's interface{} type system and reflection capabilities.
Manual detection techniques include:
// Test endpoint with prototype pollution payload
curl -X POST https://your-echo-go-api.com/api/v1/test \
-H "Content-Type: application/json" \
-d '{
"$__proto__": {
"testField": "polluted"
}
}'
Look for responses that include the polluted fields or application behavior changes that indicate successful pollution.
Echo Go-Specific Remediation
Remediating Prototype Pollution in Echo Go requires both input validation and secure coding practices. The most effective approach combines Echo Go's built-in validation features with explicit prototype pollution protection.
First, implement strict input validation using Echo Go's validator middleware:
import "github.com/go-playground/validator/v10"
func main() {
e := echo.New()
// Create validator with custom rules
v := validator.New()
// Add prototype pollution protection
v.RegisterValidation("no_prototype_pollution", func(fl validator.FieldLevel) bool {
if m, ok := fl.Field().Interface().(map[string]interface{}); ok {
for key := range m {
if strings.Contains(key, "__proto__") ||
strings.Contains(key, "constructor") ||
strings.Contains(key, "prototype") {
return false
}
}
}
return true
})
e.Validator = &CustomValidator{v}
e.POST("/api/v1/resource", validateInputHandler)
e.Start(":8080")
}
type InputPayload struct {
Data map[string]interface{} `json:"data" validate:"no_prototype_pollution"`
}
func validateInputHandler(c echo.Context) error {
var input InputPayload
if err := c.Bind(&input); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid input")
}
if err := c.Validate(input); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Input contains forbidden keys")
}
return c.JSON(http.StatusOK, map[string]string{"status": "success"})
}
For Echo Go applications that require flexible JSON handling, implement a custom binder that sanitizes input:
type SafeBinder struct{}
func (sb *SafeBinder) Bind(i interface{}, c echo.Context) error {
if err := c.Bind(i); err != nil {
return err
}
// Sanitize map[string]interface{} structures
if m, ok := i.(*map[string]interface{}); ok {
sanitizeMap(m)
}
return nil
}
func sanitizeMap(m *map[string]interface{}) {
for key := range *m {
if shouldBlockKey(key) {
delete(*m, key)
}
// Recursively sanitize nested maps
if nestedMap, ok := (*m)[key].(map[string]interface{}); ok {
sanitizeMap(&nestedMap)
}
}
}
func shouldBlockKey(key string) bool {
blockedPrefixes := []string{"__proto__", "constructor", "prototype"}
for _, prefix := range blockedPrefixes {
if strings.Contains(key, prefix) {
return true
}
}
return false
}
Register this custom binder in your Echo Go application:
e.Binder = &SafeBinder{}