【问题标题】:Is ActiveRecord's "order" method vulnerable to SQL injection?ActiveRecord 的“order”方法是否容易受到 SQL 注入的影响?
【发布时间】:2013-07-25 13:47:17
【问题描述】:

我知道在调用 .where 时使用内插字符串是不安全的。

例如这个:

Client.where("orders_count = #{params[:orders]}")

应该改写为:

Client.where("orders_count = ?", params[:orders])

调用.order 时使用内插字符串是否安全?如果不是,下面应该如何改写?

Client.order("#{some_value_1}, #{some_value_2}")

【问题讨论】:

  • 在数组中 .order("#{field} #{ordering}") 它就像一个魔术!谢谢

标签: sql ruby-on-rails-4


【解决方案1】:

是的,ActiveRecord 的“order”方法易受 SQL 注入攻击。

不,在调用.order 时使用内插字符串不安全。

以上对我问题的回答已得到Aaron Patterson 的确认,他将我指向http://rails-sqli.org/#order。从该页面:

在 ORDER BY 子句中利用 SQL 注入很棘手,但是 CASE语句可用于测试其他字段,切换排序 真假列。虽然它可能需要许多查询,但攻击者 可以确定字段的值。

因此,手动检查前往order 的任何内容很重要;也许通过使用类似于@dmcnally 建议的方法。

谢谢大家。

【讨论】:

  • 嘿@Mike 我问了这个问题,因为我不明白它是如何工作的。你能帮忙吗:stackoverflow.com/questions/28630381/…
  • 我认为 Rails 在传递原始字符串时会检查该列是否存在。清理可能仍然是一个好主意,因为理论上这可以用于 DDOS 攻击,如果有一个大的结果集并且你按没有良好索引的东西排序。
【解决方案2】:

简短的回答是你需要清理你的输入。

如果您计划插入的字符串来自不受信任的来源(例如 Web 浏览器),那么您需要首先将它们映射到受信任的值。你可以通过哈希来做到这一点:

# Mappings from known values to SQL
order_mappings = {
  'first_name_asc'  => 'first_name ASC',
  'first_name_desc' => 'first_name DESC',
  'last_name_asc'   => 'last_name ASC',
  'last_name_desc'  => 'last_name DESC',
}

# Ordering options passed in as an array from some source:
order_options = ['last_name_asc', 'first_name_asc']

# Map them to the correct SQL:
order = order_options.map{|o| order_mappings[o] }.compact.join(', ')
Client.order(order)

【讨论】:

  • 这很有用,谢谢。我的实际订单查询比问题更复杂,它需要任意用户输入。理想情况下,我想像where 那样使用数组语法。总而言之,你是说我必须自己做这项工作吗? :)
【解决方案3】:

@Mike 的解释是正确的。 @dmcnally 解决方法会起作用。我正在遵循 [Railscast][1] http://railscasts.com/episodes/228-sortable-table-columns

中提到的稍微不同的路径

简而言之,如果您可以在控制器中构造一个私有方法,以净化用户输入:

  1. 按表列的名称排序:

        private 
    
        def sort_column
           Client.column_names.include?(params[:sort]) ? params[:sort] : "first_name"
        end
    
  2. 按其他标准排序,然后使用如下白名单结构:

    def sort_direction
        %w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
    end
    

然后您的控制器方法应如下所示:

    Client.all.order(sort_column + " " + sort_direction)

只是去罗马的另一条路。希望这有帮助。

【讨论】:

    【解决方案4】:

    让我们试试这个!

    # app/models/concern/ext_active_record.rb
    module ExtActiveRecord
        extend ActiveSupport::Concern
    
        included do
            scope :sortable, -> (params) do
                return unless params[:sort_by] && params[:sort_dir]
                reorder("#{params[:sort_by]}" => "#{params[:sort_dir]}")
            end
        end
    end
    
    # app/models/user.rb
    class User < ActiveRecord::Base
        include ExtActiveRecord
        # ....
    end
    
    # app/controllers/user_controller.rb
    class UserController < ApplicationController
        def index
            @users = User.sortable(params).page(params[:page]).per(params[:per])
        end
    end
    

    【讨论】:

      【解决方案5】:

      Client.order("#{some_value_1}, #{some_value_2}")

      应该写成

      order = sanitize_sql_array(['%s, %s', some_value_1, some_value_2])
      Client.order(order)
      

      【讨论】:

      • 这不安全 Mike,创建一个你不介意丢失的虚拟表并执行以下操作:Client.order('id asc; drop table dummy;') 你可能会感到惊讶
      • 你是对的,戴夫。 Model..send(:sanitize_sql_array, ['mike %s', '; DROP TABLE X']) =&gt; "mike ; DROP TABLE X"。那么我们如何像使用where 一样确保它的安全?
      • @dmcnally: "ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: cannot insert multiple commands into aprepared statement"
      • ActiveRecord 并不总是使用准备好的语句,但@Dorian 显示了其中一个好处:即使没有使用绑定参数,它们确实会阻止运行多个查询,而您只需要一个查询。 (除非它作为 UNION 或其他东西潜入。)
      猜你喜欢
      • 1970-01-01
      • 2017-05-25
      • 2020-07-20
      • 1970-01-01
      • 1970-01-01
      • 2015-04-17
      • 1970-01-01
      • 2020-03-02
      • 2012-11-16
      相关资源
      最近更新 更多