Insecure file upload - Files Limit - Go

Insecure file upload - Files Limit - Go

Need

Enforce file upload limits and implement proper file management

Context

  • Usage of Go 1.16 for building high-performance and scalable applications
  • Usage of gin-gonic/gin for building web applications in Go
  • Usage of net/http for handling HTTP requests and responses in a Node.js application

Description

Non compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"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 is a simple file upload handler in a Go application using the Gin web framework. When a POST request is made to the "/upload" endpoint, it attempts to retrieve a file from the form data and save it to the "uploads" directory.

The vulnerability lies in the fact that there are no restrictions on the number of files that can be uploaded. This can potentially lead to the server being overwhelmed with an excessive number of files, consuming storage space and potentially causing performance degradation or even a Denial of Service (DoS) if the server runs out of storage.

Moreover, the code does not check if a file with the same name already exists in the "uploads" directory. If a file with the same name is uploaded, it will overwrite the existing file without any warning or notice. This can potentially lead to data loss if important files are accidentally overwritten.

Steps

  • Implement a limit on the number of files that can be uploaded per unit of time.
  • Track the number of files uploaded within a specific time frame, such as per minute or per hour.
  • If the limit is reached, reject the upload request and return an appropriate error message to the user.
  • Consider implementing a mechanism to delete old files after a certain period of time to prevent excessive storage usage.
  • Ensure that the uploaded files are stored in a secure location with proper access controls to prevent unauthorized access.

Compliant code

        package main

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

var uploadTimes map[string][]time.Time

func main() {
	r := gin.Default()
	uploadTimes = make(map[string][]time.Time)

	r.POST("/upload", func(c *gin.Context) {
		ip := c.ClientIP()
		uploadTimes[ip] = append(uploadTimes[ip], time.Now())

		// Limit the number of uploads to 10 per hour
		if len(uploadTimes[ip]) > 10 {
			// Remove timestamps older than 1 hour
			oneHourAgo := time.Now().Add(-1 * time.Hour)
			for i, uploadTime := range uploadTimes[ip] {
				if uploadTime.Before(oneHourAgo) {
					uploadTimes[ip] = uploadTimes[ip][i:]
					break
				}
			}

			// If there are still more than 10 uploads in the past hour, reject the request
			if len(uploadTimes[ip]) > 10 {
				c.String(http.StatusTooManyRequests, "Too many file uploads. Please try again later.")
				return
			}
		}

		file, _ := c.FormFile("file")
		c.SaveUploadedFile(file, "uploads/"+file.Filename)

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

	r.Run()
}
        
        

The code above is a modification of the original code to limit the number of file uploads per hour. It uses a map to track the upload times for each client IP address. When a file upload request is received, it first checks the number of uploads from the client IP in the past hour. If the limit of 10 uploads per hour is exceeded, it rejects the request and returns a "Too many file uploads. Please try again later." message.

The code also includes a mechanism to remove timestamps older than 1 hour from the tracking map, to prevent it from growing indefinitely.

This solution helps to mitigate the risk of a Denial of Service (DoS) attack by limiting the rate of file uploads. However, it does not address the issue of old files not being deleted from the server. This would require a separate mechanism, such as a background job that periodically deletes files older than a certain age.

Also, it's important to note that this solution uses the client's IP address to track the number of uploads. This may not be reliable if the client is behind a proxy or using a VPN, as multiple clients may appear to have the same IP address. In a production environment, a more robust method of identifying clients may be needed, such as user authentication.

Finally, the code assumes that the uploaded files are stored in a secure location with proper access controls. This is crucial to prevent unauthorized access to the uploaded files.

References