Skip to main content

Lack of data validation - Trust boundary violation

Need

Enforce strict data validation and trust boundaries

Context

  • Usage of Go 1.16 for building efficient and scalable applications
  • Usage of gin-gonic/gin for building web applications in Go

Description

Non compliant code

package main

import (
"github.com/gin-gonic/gin"
)

type User struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}

func main() {
r := gin.Default()
r.POST("/user", func(c *gin.Context) {
var user User
c.BindJSON(&user)
// TODO: Add data validation here
c.JSON(200, gin.H{
"message": "User data received",
"user": user,
})
})
r.Run()
}

In this code snippet, we have a Gin web server with a POST endpoint "/user". When a POST request is made to this endpoint, the server expects to receive a JSON payload that can be unmarshalled into the User struct.

The vulnerability lies in the fact that there is no data validation performed on the incoming payload. The server blindly trusts the incoming data and attempts to unmarshal it into the User struct with the c.BindJSON(&user) call. This is a Trust boundary violation because we are mixing trusted and untrusted data in the same data structure without performing any validation or sanitization.

An attacker could exploit this vulnerability by sending a malicious payload that could potentially lead to various attacks like SQL Injection, Cross-Site Scripting (XSS), Remote Code Execution (RCE), etc. depending on how the untrusted data is used in the application.

Steps

  • Separate trusted and untrusted data in different data structures or structured messages.
  • Perform data validation on untrusted data before using it.
  • Use input validation techniques such as input sanitization, input filtering, and input validation rules.
  • Implement server-side validation to ensure that only valid and expected data is accepted.
  • Use strong typing and enforce data type constraints to prevent unexpected data from being processed.
  • Implement proper error handling and logging to detect and handle any potential trust boundary violations.
  • Regularly update and patch the application and its dependencies to address any known vulnerabilities.

Compliant code

package main

import (
"github.com/gin-gonic/gin"
"net/http"
"regexp"
)

type User struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}

func main() {
r := gin.Default()
r.POST("/user", func(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// Validate ID - it should be alphanumeric
isAlphaNumeric := regexp.MustCompile(`^[A-Za-z0-9]+$`).MatchString
if !isAlphaNumeric(user.ID) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID. It should be alphanumeric"})
return
}

// Validate Email
isValidEmail := regexp.MustCompile(`^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$`).MatchString
if !isValidEmail(user.Email) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Email"})
return
}

c.JSON(200, gin.H{
"message": "User data received",
"user": user,
})
})
r.Run()
}

The updated code now includes data validation for the user input. The ShouldBindJSON function is used to bind the incoming JSON to the User struct and it will return an error if the incoming data is not in the expected format.

The ID field is validated to ensure it is alphanumeric. This is done using a regular expression that matches only alphanumeric characters. If the ID is not alphanumeric, the function will return a 400 Bad Request status with an error message.

The Email field is also validated using a regular expression to ensure it is in a valid email format. If the email is not valid, the function will return a 400 Bad Request status with an error message.

These changes ensure that only valid and expected data is accepted by the server, thus mitigating the risk of trust boundary violations.

References