在rails中转义SQL LIKE的查询

时间:2014-11-06 09:32:33

标签: sql ruby-on-rails activerecord escaping

在rails源代码(https://github.com/rails/rails/blob/fe4b0eee05f59831e1468ed50f55fbad0ce11e1d/activerecord/lib/active_record/sanitization.rb#L112)中有一个sanitize_sql_like方法(我希望)在使用SQL LIKE之前清理字符串

然而,我似乎无法使用它,因为Rails说这种方法并不存在。

我的字符串中有一个撇号,查询是

@query = "Joe's"
Model.where("lower(field) LIKE ?", "%#{@query}%") 

使用ActiveRecord::Base.sanitize没有帮助,因为查询没有结果。

如何逃避@query并保护我的SQL安全?

2 个答案:

答案 0 :(得分:5)

我使用ActiveRecord::Sanitization::ClassMethods解决了同样的问题(使用MySQL)来正确清理用户输入。不幸的是,ActiveRecord :: Sanitization :: ClassMethods中定义的方法被声明为protected,因此它们只能在Model类的范围内访问。 ActiveRecord :: Sanitization :: ClassMethods中定义的方法混合到所有Model类中,因此可以直接在Model的范围内使用它们。这要求您在模型上定义类/实例方法或scopeActiveRecord scope)以使用它们,而不是像在示例中那样在外部使用它们。但是,从设计角度来看,最好将查询逻辑封装在模型中。

此解决方案还具有以下优点:不仅可以转义单引号字符,还可以转义将由SQL查询解释的其他字符,例如'%'性格(以及其他)。这应该通过转义可能导致值被解释而不是被视为文字的其他字符来正确地防止SQL注入。 ActiveRecord::Sanitization::ClassMethods中还定义了一些额外的清理方法,这些方法对于在其他SQL查询上下文中嵌入用户输入(或其他受污染的输入)非常有用。这是一个在MySQL上测试的工作解决方案。

class Model  < ActiveRecord::Base
  # Finds a Model by case-insensitive substring match on Model.field
  #
  # @param query [String] A value to use in the substring match.
  # @return [ActiveRecord::Relation] An `Relation` of `Model`s whose
  #   `field` includes the `query` substring.
  scope :find_by_field_substring, ->(query) do
    where(arel_table[:field].matches("%#{sanitize_sql_like(query)}%"))
  end
end 

然后,您可以像这样访问scope

Model.find_by_field_substring "Joe's"
=> #<ActiveRecord::Relation [#<Model id: 1, field: "Joe's">]>

ActiveRecord范围和ActiveRecord::Relations的使用都有很好的记录,但可能只在较新版本的Rails中可用。

请注意,您的数据库可能需要sanitize_sql_like方法的第二个参数来指定与&#39; \&#39;不同的转义字符。

另请注意,如果它在NO_BACKSLASH_ESCAPES模式下运行,那么这样的转义对MySQL不起作用,因为它会强制使用不同的机制来转义值。如果您正在使用NO_BACKSLASH_ESCAPES模式,请参阅this answer

我还没有测试过这个部分,但是使用位于ActiveRecord下面的SQL AST构建器Arel,可能也可以使用初始示例中建议的样式,只要它在模型中定义即可。类。

class Model  < ActiveRecord::Base
  scope :find_by_field_substring, ->(query) do
    where("lower(field) LIKE ?", "%#{sanitize_sql_like(query)}%") 
  end
end

答案 1 :(得分:4)

如果您正确使用where,它将自动转义输入

@query = "Joe's"
Model.where("lower(field) LIKE ?", "%#{@query}%") 

请注意您的查询错误。你有一个lower()运算符,然后传递一个非小写的输入。查询将始终返回0.

此外,lower()会降低数据库使用索引的能力。在大多数数据库中,LIKE已经不区分大小写(除了PostgreSQL,你应该使用ILIKE)。

query = "Joe's"
Model.where("field LIKE ?", "%#{query}%") 

query = "Joe's"
Model.where("field ILIKE ?", "%#{query}%") 

这是真实数据库的真实示例。如您所见,输入在最终的SQL中被正确转义。

> query = "Joe's"
> User.where("lower(email) LIKE ?", "%#{query}%") 
  User Load (4.4ms)  SELECT "users".* FROM "users"  WHERE (lower(email) LIKE '%Joe''s%')
 => #<ActiveRecord::Relation []>