【问题标题】:Rails, how to sanitize SQL in find_by_sqlRails,如何在 find_by_sql 中清理 SQL
【发布时间】:2011-11-01 00:26:37
【问题描述】:

有没有办法在rails方法find_by_sql中清理sql?

我试过这个解决方案: Ruby on Rails: How to sanitize a string for SQL when not using find?

但它失败了

Model.execute_sql("Update users set active = 0 where id = 2")

它抛出一个错误,但是执行了 sql 代码并且 ID 为 2 的用户现在有一个禁用的帐户。

简单的find_by_sql也行不通:

Model.find_by_sql("UPDATE user set active = 0 where id = 1")
# => code executed, user with id 1 have now ban

编辑:

我的客户要求在管理面板中创建该功能(通过 sql 选择)以进行一些复杂的查询(连接、特殊条件等)。所以我真的很想find_by_sql那个。

第二次编辑:

我想实现“邪恶”的 SQL 代码不会被执行。

在管理面板中,您可以输入 query -> Update users set admin = true where id = 232 我想阻止任何 UPDATE / DROP / ALTER SQL 命令。 只想知道,这里只能执行SELECT。

经过一些尝试,我得出结论 sanitize_sql_array 很遗憾不要那样做。

有没有办法在 Rails 中做到这一点?

抱歉给您带来了困扰。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 activerecord find-by-sql


    【解决方案1】:

    试试这个:

    connect = ActiveRecord::Base.connection();
    connect.execute(ActiveRecord::Base.send(:sanitize_sql_array, "your string"))
    

    您可以将其保存在变量中并用于您的目的。

    【讨论】:

    • 结果:protected method 'sanitize_sql_array' called for ActiveRecord::Base:Class
    • ..或者你可以使用这个:ActiveRecord::Base.send(:sanitize_sql_array, "your string")
    • 最后一个工作......但它仍然执行“邪恶”的 sql 代码。 ;)
    • 所以,如果您只想更新记录 - 像 DanneManne 所说的那样使用 update_all ;) (apidock.com/rails/ActiveRecord/Base/update_all/class)
    【解决方案2】:

    我为此做了一个小sn-p,你可以把它放在初始化器中。

    class ActiveRecord::Base  
      def self.escape_sql(array)
        self.send(:sanitize_sql_array, array)
      end
    end
    

    现在你可以用这个来逃避你的查询:

    query = User.escape_sql(["Update users set active = ? where id = ?", true, params[:id]])
    

    您可以随意调用查询:

    users = User.find_by_sql(query)
    

    【讨论】:

    • 这可行,但请注意,您不需要“发送”。当调用者是同一个类时,接收者允许调用受保护的方法。也就是说,您只需要将方法定义为:“def self.escape_sql(obj); sanitize_sql_array obj; end;” (从 SO cmets down 开始使用分号允许返回行)
    • 感谢您提供额外的帮助并举例说明如何使用它。
    • 另请注意,它不适用于数组。它使它们变平:(
    【解决方案3】:

    更通用:

    class ActiveRecord::Base  
      def self.escape_sql(clause, *rest)
        self.send(:sanitize_sql_array, rest.empty? ? clause : ([clause] + rest))
      end
    end
    

    这个可以让你调用它,就像你在 where 子句中输入一样,没有额外的括号,并使用数组样式?或散列式插值。

    【讨论】:

    • 谢谢,这很好用。只需在初始化程序中添加即可。
    【解决方案4】:
    User.find_by_sql(["SELECT * FROM users WHERE (name = ?)", params])
    

    来源:http://blog.endpoint.com/2012/10/dont-sleep-on-rails-3-sql-injection.html

    【讨论】:

    • 通常最好包含对您提供的代码的解释,而不是简单地链接到解释它的博客。如果该博客的所有者将帖子更新为不再包含您提供的代码、删除该帖子或删除该博客会怎样?
    • 同意你的观点@MattD。以后我会按照你的建议去做。
    【解决方案5】:

    虽然这个示例是针对 INSERT 查询的,但可以对 UPDATE 查询使用类似的方法。原始 SQL 批量插入:

    users_places = []
    users_values = []
    timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
    params[:users].each do |user|
        users_places << "(?,?,?,?)" # Append to array
        users_values << user[:name] << user[:punch_line] << timestamp << timestamp
    end
    
    bulk_insert_users_sql_arr = ["INSERT INTO users (name, punch_line, created_at, updated_at) VALUES #{users_places.join(", ")}"] + users_values
    begin
        sql = ActiveRecord::Base.send(:sanitize_sql_array, bulk_insert_users_sql_arr)
        ActiveRecord::Base.connection.execute(sql)
    rescue
        "something went wrong with the bulk insert sql query"
    end
    

    这里是reference to sanitize_sql_array method in ActiveRecord::Base,它通过转义字符串中的单引号来生成正确的查询字符串。例如,妙语“不要让他们让你失望”将变成“不要让他们让你失望”。

    【讨论】:

      【解决方案6】:

      我更喜欢使用关键参数。在您的情况下,它可能如下所示:

        Model.find_by_sql(["UPDATE user set active = :active where id = :id", active: 0, id: 1])
      

      请注意,您只将一个参数传递给:find_by_sql 方法 - 它是一个数组,包含两个元素:字符串查询和带参数的哈希(因为它是我们最喜欢的 Ruby,您可以省略大括号)。

      【讨论】:

        猜你喜欢
        • 2011-03-01
        • 2016-03-27
        • 2019-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-16
        • 2010-11-25
        • 2023-03-15
        相关资源
        最近更新 更多