Insecure file upload - Files Limit - Ruby

Insecure file upload - Files Limit - Ruby

Need

Enforce file upload limits and implement proper file management

Context

  • Usage of Ruby for developing web applications and scripting
  • Usage of Ruby on Rails for building web applications

Description

Non compliant code

        class UploadsController < ApplicationController
  def create
    params[:files].each do |file|
      UploadedFile.create(file: file)
    end
    redirect_to uploads_path
  end
end
        
        

This code represents a file upload feature in a Ruby on Rails application. The create action in the UploadsController is responsible for handling file uploads.

Here's how it works:

- It accepts a collection of files as a parameter ( params[:files]).
- For each file in the collection, it creates a new instance of the UploadedFile model, storing the file.
- After all files have been uploaded, it redirects the user to the uploads page.

The vulnerability lies in the fact that there is no limit to the number of files a user can upload. This means that a malicious user could potentially upload a large number of files, filling up the server's storage and potentially causing a Denial of Service (DoS) attack.

Additionally, the system does not delete any previously uploaded files when a new one is uploaded. This means that over time, old and potentially unneeded files could accumulate on the server, further exacerbating the potential for storage issues.

Steps

  • Implement a limit on the number of files that can be uploaded per unit of time.
  • Consider implementing a file retention policy to delete old files from the server.
  • Validate the uploaded files to ensure they are of the expected file types and do not contain any malicious content.
  • Implement proper error handling and user feedback for failed file uploads.
  • Consider implementing file size restrictions to prevent excessively large files from being uploaded.

Compliant code

        class UploadsController < ApplicationController
  MAX_FILES = 10 # Maximum number of files that can be uploaded per unit of time
  MAX_FILE_SIZE = 5.megabytes # Maximum size of each file

  def create
    if params[:files].size > MAX_FILES
      flash[:error] = "You can only upload a maximum of #{MAX_FILES} files at a time."
      redirect_to uploads_path
      return
    end

    params[:files].each do |file|
      if file.size > MAX_FILE_SIZE
        flash[:error] = "Each file must be less than #{MAX_FILE_SIZE} MB."
        redirect_to uploads_path
        return
      end

      uploaded_file = UploadedFile.create(file: file)
      if uploaded_file.errors.any?
        flash[:error] = uploaded_file.errors.full_messages.join(", ")
        redirect_to uploads_path
        return
      end
    end

    flash[:success] = "Files uploaded successfully."
    redirect_to uploads_path
  end
end
        
        

The updated code now includes a limit on the number of files that can be uploaded at a time, as well as a maximum file size for each file.

The MAX_FILES constant is set to 10, meaning that users can only upload up to 10 files at a time. If a user tries to upload more than this, they will be redirected back to the uploads page with an error message.

The MAX_FILE_SIZE constant is set to 5 megabytes. If a user tries to upload a file that is larger than this, they will be redirected back to the uploads page with an error message.

When a file is uploaded, the create method is called on the UploadedFile model. If there are any errors with this (for example, if the file is of an invalid type), these errors are added to the flash[:error] hash and the user is redirected back to the uploads page.

If all files are uploaded successfully, a success message is displayed to the user.

This code does not yet include a file retention policy to delete old files from the server. This would need to be implemented separately, perhaps as a background job that runs periodically to delete files that are older than a certain age.

References