Skip to main content

Insecure file upload

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? "<script>"
flash[:error] = "File contains malicious code"
redirect_to uploads_path
return
end

# Use secure location for file storage
secure_location = Rails.root.join('private', 'uploads', uploaded_io.original_filename)
File.open(secure_location, 'wb') do |file|
file.write(uploaded_io.read)
end
else
flash[:error] = "Invalid file type"
end

redirect_to uploads_path
end

The updated code includes a validation for the file type. It checks the file extension and only allows specific extensions (in this case, .jpg, .png, .gif, .pdf). You can modify the allowed_extensions array based on your requirements.

The code also includes a simple validation for the file content. It checks if the file contains a &lt;script> tag, which is a common way to include malicious code in a file. If the file contains a &lt;script> tag, the code will not save the file and will redirect the user back to the upload page with an error message. Note that this is a simple example, and you may need a more complex validation based on your requirements.

Finally, the code saves the file in a secure location (private/uploads) instead of the public directory. This prevents unauthorized access to the uploaded files.

References