Prevention of technical information leaks and error exposure
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
var json struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if json.Username != "admin" || json.Password != "admin" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Incorrect Username or Password"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "You are logged in"})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
The above code is a simple login backend application using the Gin Gonic framework in Go. The
/login
endpoint accepts a POST request with a JSON body containing
username
and
password
fields.
The application is vulnerable to a technical information leak through error messages. If a request is malformed or missing required fields, the server throws an error and returns the error message to the client. For example, if the JSON body does not contain the
username
or
password
fields, the server responds with a
400 Bad Request
status and the error message
Key: 'username' Error:Field validation for 'username' failed on the 'required' tag
or
Key: 'password' Error:Field validation for 'password' failed on the 'required' tag
respectively.
This behaviour can expose sensitive technical details about the server and the application, which can be exploited by attackers to gather information about the system and potentially find other vulnerabilities. For instance, an attacker can send requests with different malformed JSON bodies to the
/login
endpoint and observe the error messages to infer details about the server's validation logic.
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
// Implement rate limiting
r.Use(gin.Limit(1 << 14))
r.POST("/login", func(c *gin.Context) {
var json struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindJSON(&json); err != nil {
// Log the error on the server-side
log.Printf("Error: %s", err.Error())
// Return a generic error message
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
if json.Username != "admin" || json.Password != "admin" {
// Log the error on the server-side
log.Printf("Error: Incorrect Username or Password")
// Return a generic error message
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "You are logged in"})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
The updated code includes several improvements to address the identified vulnerability:
1. Rate Limiting: The
gin.Limit
middleware is used to limit the number of incoming requests. This helps to prevent abuse of the endpoints by sending massive requests.
2. Error Handling: Instead of returning detailed error messages in the response to the client, a generic error message is returned. This prevents sensitive information leakage. The detailed error messages are logged on the server-side for debugging purposes.
3. Input Validation: The
ShouldBindJSON
function is used to validate the user input. This helps to prevent any unexpected behavior or attacks.
4. Authentication: The code checks if the username and password are correct. If not, a generic error message is returned and the detailed error message is logged on the server-side.
By implementing these improvements, the application becomes more secure and less prone to information leakage and abuse of the endpoints.