在rails中转义SQL LIKE的查询

问题描述 投票:5回答:2

在rails source(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安全?

sql ruby-on-rails activerecord escaping
2个回答
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方法的第二个参数来指定与“\”不同的转义字符。

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

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

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

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 []> 
© www.soinside.com 2019 - 2024. All rights reserved.