如何在 Rails 中清理 sql 片段

Posted

技术标签:

【中文标题】如何在 Rails 中清理 sql 片段【英文标题】:How to sanitize sql fragment in Rails 【发布时间】:2011-03-01 20:13:45 【问题描述】:

我必须清理 sql 查询的一部分。我可以这样做:

class << ActiveRecord::Base
  public :sanitize_sql
end

str = ActiveRecord::Base.sanitize_sql(["AND column1 = ?", "two's"], '')

但这并不安全,因为我暴露了受保护的方法。有什么更好的方法?

【问题讨论】:

你能给我们更多的背景信息吗? sanitize_sql 和朋友经常在 AR::Base 派生类中调用,无需更改可见性 这是一个很好且有效的观点。当我使用某人的私有或受保护方法时,我只会畏缩。 【参考方案1】:

ActiveRecord::Base.connection.quote 在 Rails 3.x 中发挥作用

【讨论】:

【参考方案2】:

你可以使用:

ActiveRecord::Base::sanitize_sql(string)

【讨论】:

这代表ActiveRecord::Base.connection.quote(至少在Rails 4中) 已弃用。新的消毒方法here。请参阅 Bryan 的 answer 我将此答案从已弃用的版本 (sanitize) 更新为 Rails 6 的当前工作版本 (sanitize_sql)。如果有人想全面了解所有消毒方法,我建议the docs。【参考方案3】:

这个问题没有指定答案必须来自ActiveRecord,也没有指定应该是哪个版本的Rails。出于这个原因(并且因为它是***和少数几个)关于如何清理 Rails 中的参数的答案......


这里有一个适用于 Rails 4 的解决方案:

ActiveRecord::Sanitization::ClassMethods 中有sanitize_sql_for_conditions 及其另外两个别名sanitize_conditionssanitize_sql。这三个人做着完全相同的事情。

sanitize_sql_for_conditions

接受一个数组、散列或 SQL 条件字符串并清理 将它们放入一个有效的 SQL 片段中用于 WHERE 子句

在 ActiveRecord 中你也有

sanitize_sql_for_assignment哪个

接受一个数组、散列或 SQL 条件字符串并对其进行清理 用于 SET 子句的有效 SQL 片段。

上述方法默认包含在 ActiveRecord::Base 中,因此包含在任何 ActiveRecord 模型中

见docs


不过,在 ActionController 中,您有 ActionController::Parameters,它允许您

选择哪些属性应该被列入白名单以进行大规模更新和 从而防止意外暴露不应该暴露的东西。 为此提供了两种方法:requirepermit

   

params = ActionController::Parameters.new(user:  name: 'Bryan', age: 21 )
req  = params.require(:user) # will throw exception if user not present
opt  = params.permit(:name)  # name parameter is optional, returns nil if not present
user = params.require(:user).permit(:name, :age) # user hash is required while `name` and `age` keys are optional

“参数魔法”称为强参数 (docs here),您可以使用它在控制器中清理参数,然后再将其发送到模型。

上述方法默认包含在ActionController::Base 中,因此包含在任何 Rails 控制器中

我希望这对任何人都有帮助,哪怕只是为了学习和揭开 Rails 的神秘面纱! :)

【讨论】:

requirepermit 是针对 SQL 注入清理参数,还是仅验证它们的存在? 它进行零消毒,只是验证存在 @Matt:Jimmy 是对的,requirepermit 自己不要进行任何消毒。但是ActionController::Parameters.new 进行了清理,因此您的所有控制器都应该已经清理了所有参数。稍后有时间我会更新我的答案,因为我还发现了这个非常酷的 gem,叫做 rails-html-sanitizer github.com/rails/rails-html-sanitizer 谢谢布莱恩。我正在向控制器发送大量 JSON,并且正在处理 3 秒以上的响应时间。我可以通过消除 JSON 中的冗余将这些时间减少到 2 秒,并通过不实例化 ActiveRecord 对象进一步将其降低到 500 毫秒以下。只是想确定我暴露了安全风险! 请注意,上面的清理方法是受保护的类方法,因此您需要从 AR 类中调用它们,或者通过您自己的 AR 类中的公共类方法公开它们。【参考方案4】:

从 rails 5 开始,推荐的方法是使用:ActiveRecord::Base.connection.quote(string)

如此处所述:https://github.com/rails/rails/issues/28947

ActiveRecord::Base::sanitize(string) 已弃用

【讨论】:

不推荐。请改用 Bryan 的 answer 中的 sanitation api。【参考方案5】:

请注意,在清理 SQL WHERE 条件时,最好的解决方案 sanitize_sql_hash_for_conditions,因为它正确处理了 NULL 条件(例如,如果为 nil,则会生成 IS NULL 而不是 = NULL属性已通过)。

由于某种原因,它在 Rails 5 中已被弃用。所以我推出了一个面向未来的版本,请参见此处:https://***.com/a/53948665/165673

【讨论】:

以上是关于如何在 Rails 中清理 sql 片段的主要内容,如果未能解决你的问题,请参考以下文章

以下代码片段是不是容易受到 Rails 5 中 SQL 注入的影响?

Rails:放置在哪里以及如何调用预定的清理方法

Rails:旧数据与新数据不匹配时如何更新片段缓存

在 Rails 上为连接、限制、选择等(不是条件)的 SQL 片段安全地转义字符串

Ruby on Rails:不使用 find 时如何为 SQL 清理字符串?

如何在 Rails 中链接原始 SQL 查询或如何从 Rails 中的原始 SQL 查询返回 ActiveRecord_Relation?