Inappropriate coding practices - invalid file - Ruby

Inappropriate coding practices - invalid file - Ruby

Need

Enforcement of proper file validation and verification

Context

  • Usage of Ruby for building web applications and scripting
  • Usage of Rails framework for building web applications

Description

Non compliant code

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

The above code is a simple file upload function in a Ruby on Rails application. It takes a file from the user input, and then writes it to the public/uploads directory with the original filename.

This code is vulnerable because it doesn't validate the content of the uploaded file against its extension. This means that a user could upload a malicious file with a disguised extension. For example, a user could upload a file named picture.jpg, but the actual content of the file could be a harmful script.

When the application uses this file (for example, if it tries to display it as an image), unexpected and potentially harmful actions could occur. This could lead to various types of attacks, such as code execution, data leakage, or even complete system compromise.

To fix this vulnerability, the application should implement a check to ensure that the file content matches the file extension before saving it to the system.

Steps

  • Validate the file extension before saving it to the repository.
  • Use a whitelist approach to only allow specific file extensions that are considered safe.
  • Implement server-side file type verification to ensure that the file content matches its extension.
  • Consider using a file scanning service or library to detect malicious content in uploaded files.
  • Sanitize the file name to prevent any potential path traversal attacks.
  • Store the files in a secure location outside of the public directory to prevent direct access.
  • Implement access controls to restrict who can upload and access files.
  • Regularly review and remove any unnecessary or unused files from the repository.

Compliant code

        def upload_file
  uploaded_io = params[:file]
  file_extension = File.extname(uploaded_io.original_filename)
  whitelist = %w(.jpg .jpeg .png .gif .pdf)

  # Validate file extension
  if whitelist.include? file_extension
    sanitized_filename = sanitize_filename(uploaded_io.original_filename)

    # Save file to a secure location
    File.open(Rails.root.join('secure_uploads', sanitized_filename), 'wb') do |file|
      file.write(uploaded_io.read)
    end
  else
    flash[:error] = "Invalid file extension. Only #{whitelist.join(", ")} are allowed."
    redirect_to :back
  end
end

def sanitize_filename(filename)
  # Get only the filename, not the whole path (for IE)
  just_filename = File.basename(filename)
  # Replace all non-alphanumeric, underscore or periods with underscore
  just_filename.gsub(/[^A-Za-z0-9\\.\\-]/, '_')
end
        
        

The updated code includes several security measures to prevent the vulnerability:

1. File Extension Validation: The code checks the file extension against a whitelist of allowed extensions. If the file extension is not in the whitelist, the file upload is rejected and the user is redirected back with an error message.

2. File Name Sanitization: The filename is sanitized to prevent path traversal attacks. This is done by replacing all non-alphanumeric, underscore, or period characters with an underscore.

3. Secure File Storage: The file is saved in a secure location ( secure_uploads) outside of the public directory. This prevents direct access to the uploaded files.

Remember to implement server-side file type verification and consider using a file scanning service or library to detect malicious content in uploaded files. Also, implement access controls to restrict who can upload and access files, and regularly review and remove any unnecessary or unused files from the repository.

References