Insecure file upload - Ruby

Insecure file upload - Ruby

Need

Secure file upload process

Context

  • Usage of Ruby 2.0+ for developing Ruby applications
  • Usage of Rails framework for building web applications

Description

Non compliant code

        def upload
  uploaded_io = params[:file]
  File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
    file.write(uploaded_io.read)
  end
  redirect_to uploads_path
end
        
        

The above code is a basic implementation of a file upload feature in a Ruby on Rails application. This method is vulnerable to insecure file uploads.

The upload method takes a file from the HTTP parameters and writes it to the public/uploads directory. The .original_filename method is used to get the name of the file from the client, and the file is written without any validation of its content or its extension.

The vulnerability lies in the fact that there are no checks for the file extension or the content of the file. This means that an attacker can upload a file with a double extension (like image.jpg.php) or an unsafe extension (like .html, .php, or .exe). These files can contain malicious code, which could be executed if the file is accessed.

Also, since the system does not validate whether the file is free from malicious code, an attacker could upload a file that appears to be harmless (like an image or a text file), but that actually contains hidden malicious code. This code could be executed when the file is accessed, leading to serious security issues.

Steps

  • Implement file type validation to only allow specific file extensions.
  • Use a secure file storage location instead of the public directory.
  • Implement file content validation to check for malicious code.
  • Consider using a third-party library or service for file upload handling.

Compliant code

        def upload
  uploaded_io = params[:file]
  # Validate file type
  file_extension = File.extname(uploaded_io.original_filename).downcase
  allowed_extensions = ['.jpg', '.png', '.gif', '.pdf'] # Add or remove extensions based on your requirements

  if allowed_extensions.include? file_extension
    # Validate file content
    # This is a simple example, you may need a more complex validation based on your requirements
    if uploaded_io.read.include? "