ActiveAdmin: Prevent command execution in CSV export file

From FVue
Jump to: navigation, search

Problem

A malicious user can change a database field so that when an administrator uses the Export functionality and opens the exported CSV in a spreadsheet editor such as Excel, code may be run on the administrator's computer. Alternatively, a malicious or compromised administrator can add or modify users with formulas in various fields to target other application administrators and users.

See: https://wiki.mozilla.org/images/6/6f/Phpmyadmin-report.pdf

Solution

When performing a CSV Export, for any cell that starts with an =, -, ", @, or +, add a space to the beginning and remove any tab characters (0x09) in the cell. Alternatively, prepend each cell field with a single quote, so that their content will be read as text by a spreadsheet editor.

See: https://wiki.mozilla.org/images/6/6f/Phpmyadmin-report.pdf

Environment:

  • activeadmin-1.3.1
  • rails-5.2.0
# config/initializers/activeadmin_csv_escape.rb
#
module ActiveAdmin
  class CSVBuilder
    def build_row(resource, columns, options)
      columns.map do |column|
        encode escape(call_method_or_proc_on(resource, column.data)), options
      end
    end
 
    # Escape cell content to prevent arbitrary command execution (in Excel)
    #
    #     "Recommendation:
    #     When performing a CSV Export, for any cell that starts with an =,
    #     -, ", @, or +, add a space to the beginning and remove any tab
    #     characters (0x09) in the cell.  Alternatively, prepend each cell
    #     field with a single quote, so that their content will be read as
    #     text by a spreadsheet editor."
    #     See: https://wiki.mozilla.org/images/6/6f/Phpmyadmin-report.pdf
    #
    def escape(string)
      # Check if cell starts with an =, -, ", @, +
      %w[ = - " @ + ].each do |char|
        if string.to_s.start_with? char
          # Prepend with space
          string.prepend " "
          # Replace tab (0x09) with space
          string.gsub!("\t", ' ')
        end
      end
      return string
    end
  end
end

Comments

blog comments powered by Disqus