【问题标题】:Escaping values in Rails (similar to mysql_real_escape_string())Rails 中的转义值(类似于 mysql_real_escape_string())
【发布时间】:2011-01-14 21:16:32
【问题描述】:

我知道预处理语句,但如果我使用的是原始 SQL,ActiveRecord 是否可以手动转义值?

这样的东西会很好:

self.escape("O'Malley") # O\'Malley

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    你可以这样做:

    Dude.sanitize("O'Malley")
    

    Dude.connection.quote("O'Malley")
    

    两者的结果相同:=> "'O''Malley'"

    【讨论】:

    • 如果其他人想知道,是的,它们完全一样:sanitize calls connection.quote
    • 在 rails 5.1 中,sanitize 函数已被删除,取而代之的是 sanitize_sql
    • 从 Rails 5.2.4.1 开始,或者可能在 Rails 5.2.4.1 中(至少),这些似乎没有给出相同的结果,至少使用 Postgres 作为后端。 Model.connection.quote 按预期工作(用单引号括起来,双单引号任何单引号)。 sanitize_sql 在所示示例中什么都不做(它可能会清理其他内容,但不会转义单引号)
    【解决方案2】:

    快速浏览 ActiveRecord 源代码揭示了其用于清理 [string, bind_variable[, bind_variable]] 类型的 sql 语句的方法“sanitize_sql_array”

    你可以直接调用它:

    sql = ActiveRecord::Base.send(:sanitize_sql_array, ["insert into foo (bar, baz) values (?, ?), (?, ?)", 'a', 'b', 'c', 'd'])
    res = ActiveRecord::Base.connection.execute(sql)
    

    【讨论】:

    • Jason:这是一个更好的解决方案,因为它独立于数据库。如果应用程序部署在 Heroku 上,当前接受的解决方案 (@quest) 将不起作用。
    • 四年后,sanitize_sql_array 及其表亲仍然不是公共 API 的一部分。有没有方便的公共等价物?
    【解决方案3】:

    您可以轻松地使用 mysql2 gem 来执行此操作:

    irb(main):002:0> require 'rubygems'
    => true
    irb(main):003:0> require 'mysql2'
    => true
    irb(main):004:0> Mysql2::Client.escape("O'Malley") # => "O\\'Malley"
    => "O\\'Malley"
    

    或者如果使用早期的 mysql(不是 mysql2)gem:

    irb(main):002:0> require 'rubygems'
    => true
    irb(main):003:0> require 'mysql'
    => true
    irb(main):004:0> Mysql.escape_string("O'Malley")
    => "O\\'Malley"
    

    这将允许您转义任何您想要的内容,然后插入到数据库中。您也可以使用 sanitize 方法在 Rails 应用程序中的大多数模型上执行此操作。例如,假设您有一个名为 Person 的模型。你可以的。

    Person.sanitize("O'Malley")
    

    这应该可以解决问题。

    【讨论】:

    • 使用 Mysql2 gem:Mysql2::Client.escape("O'Malley") # => "O\\'Malley"
    【解决方案4】:

    如果您不希望在使用@konus 发布的解决方案时出现额外的单引号包裹您的字符串,您可以这样做:

    Dude.connection.quote_string("O'Malley")
    

    这将返回 "O\'Malley" 而不是 "'O\'Malley'"

    【讨论】:

    • 这不会阻止你进行 SQL 注入。
    • @tvdeyen:您是否愿意说明上述内容以何种方式易受攻击,尤其是与公认的答案相比?最终,quote_string 将取决于正在使用的特定 ActiveRecord 适配器。对于 mysql2 适配器和抽象 mysql 适配器,quote 调用 quote_string 获取字符串值并将结果用引号括起来。您是否在考虑某种多字节注入或其他?谢谢。
    【解决方案5】:

    即使使用Model.find_by_sql,您仍然可以使用问号作为转义值的形式。

    只需传递一个数组,其中第一个元素是查询,后续元素是要替换的值。

    Rails API 文档中的示例:

    Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]

    【讨论】:

    • 我可以挖掘它,但实际上:你为什么要在 ActiveRelation/ActiveModel 之外插入?
    • 我正在做一个INSERT IGNORE 并且我正在更新一个语句中的多个记录以提高性能。
    【解决方案6】:

    如果有人正在寻找@jemminger 解决方案的更具体示例,这里是批量插入:

    users_places = []
    users_values = []
    timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
    params[:users].each do |user|
        users_places "(?,?,?,?)"
        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,它通过转义字符串中的单引号来生成正确的查询字符串。例如,妙语“不要让他们让你失望”将变成“不要让他们让你失望”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-16
      • 2011-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多