Skip to main content

Inappropriate coding practices - invalid file

Need

Enforcement of proper file validation and extension matching

Context

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

Description

Non compliant code

package main

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

func main() {
r := gin.Default()

r.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.SaveUploadedFile(file, "./uploads/"+file.Filename)

c.String(http.StatusOK, "File uploaded successfully.")
})

r.Run()
}

The above code represents a simple file upload functionality in a Go backend application using the Gin framework. The POST endpoint /upload receives a file from a form-data request and saves it to the ./uploads/ directory with its original filename.

This code is vulnerable because it does not validate or verify the uploaded file. The file's content is not checked to ensure it matches the file's extension. This can lead to several security risks, such as:

  1. File type mismatch: An attacker could upload a malicious script with a benign file extension (e.g., a .jpg file that is actually a .php script). The server could then unknowingly process the malicious script, leading to a potential security breach.

  2. File content verification: Without verifying the content of the file, an attacker can upload any file they want, including potentially harmful or illegal content.

  3. File extension whitelist: The server does not maintain a whitelist of acceptable file extensions. This allows any type of file to be uploaded, which could be problematic if the server has specific handling for certain file types.

The application should implement appropriate file validation checks to ensure that the content of the uploaded file matches its extension.

Steps

  • Validate the file extension before saving it to the server.
  • Use a whitelist of allowed file extensions to ensure that only valid files are accepted.
  • Implement server-side file content validation to ensure that the file content matches its extension.
  • Consider using a file signature or magic number to verify the file's content type.
  • Provide appropriate error handling and response messages for invalid files.

Compliant code

package main

import (
"github.com/gin-gonic/gin"
"io/ioutil"
"net/http"
"path/filepath"
"strings"
)

var allowedExtensions = map[string]bool{
".jpg": true,
".jpeg": true,
".png": true,
".gif": true,
// add more file extensions as per your requirements
}

func main() {
r := gin.Default()

r.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
extension := strings.ToLower(filepath.Ext(file.Filename))

if _, exists := allowedExtensions[extension]; !exists {
c.String(http.StatusBadRequest, "Invalid file extension.")
return
}

err := c.SaveUploadedFile(file, "./uploads/"+file.Filename)
if err != nil {
c.String(http.StatusInternalServerError, "Failed to upload file.")
return
}

c.String(http.StatusOK, "File uploaded successfully.")
})

r.Run()
}

The updated code now includes a whitelist of allowed file extensions. When a file is uploaded, the server checks the file's extension against this whitelist. If the extension is not in the whitelist, the server responds with a 400 Bad Request status and an "Invalid file extension." message.

The filepath.Ext function is used to get the file extension. The strings.ToLower function is used to ensure the extension check is case-insensitive.

The c.SaveUploadedFile function is now inside an error check. If the function returns an error (which could happen if the file is not valid), the server responds with a 500 Internal Server Error status and a "Failed to upload file." message.

This code does not yet include server-side file content validation or file signature verification. These would be additional steps to further secure the file upload process.

References