External control of file name or path
Need
Enforce secure file handling and prevent unauthorized access to sensitive files
Context
• Usage of Ruby for building dynamic and object-oriented applications
• Usage of Rails framework for building web applications
Description
1. Non compliant code
def upload
uploaded_io = params[:file]
File.open(Rails.root.join('public', 'uploads', params[:filename]), 'wb') do |file|
file.write(uploaded_io.read)
end
endThis code represents a file upload functionality in a Ruby on Rails application. The `upload` method takes a file from the request parameters (`params[:file]`) and writes it to a specified location on the server. The vulnerability lies in the usage of `params[:filename]` to determine the name and path of the saved file. This parameter is controlled by the user, which means an attacker can manipulate it to overwrite any file on the server that the application has write access to. For example, an attacker could set `params[:filename]` to `'../../../config/database.yml'`, which would cause the application to overwrite its own database configuration file with the contents of the uploaded file. This vulnerability is a type of Path Traversal vulnerability and it's also known as "Unrestricted File Upload". It can lead to various types of attacks like code execution, denial of service, sensitive information disclosure etc. depending on what the overwritten file does and the contents of the uploaded file.
2. Steps
• Validate the file name and path before saving it.
• Use a whitelist approach to only allow specific file extensions or formats.
• Sanitize the file name to remove any potentially malicious characters or sequences.
• Generate a unique file name to prevent overwriting existing files.
• Store the uploaded files in a secure directory outside of the public folder.
• Implement access controls to restrict unauthorized access to the uploaded files.
• Consider using a file storage service or cloud storage provider for added security and scalability.
3. Secure code example
def upload
uploaded_io = params[:file]
filename = sanitize_filename(params[:filename])
raise "Invalid file format." unless valid_file_format?(filename)
unique_filename = generate_unique_filename(filename)
secure_directory = Rails.root.join('secure_uploads')
File.open(File.join(secure_directory, unique_filename), 'wb') do |file|...The updated code includes several security measures to prevent external control of file name or path. 1. **Sanitization of filename**: The `sanitize_filename` method removes any directory paths and replaces invalid characters with underscore. This prevents directory traversal attacks. 2. **Validation of file format**: The `valid_file_format?` method checks if the file extension is in the list of allowed formats. This is a whitelist approach that only allows specific file extensions. 3. **Generation of unique filename**: The `generate_unique_filename` method generates a unique filename using the current timestamp. This prevents overwriting of existing files. 4. **Secure directory for uploads**: The uploaded files are saved in a 'secure_uploads' directory which is outside of the public folder. This prevents unauthorized access to the uploaded files. Remember to implement access controls at the application level to further restrict unauthorized access to the uploaded files. Also, consider using a file storage service or cloud storage provider for added security and scalability.
References
• 098. External control of file name or path