这是否容易受到 sql 注入的影响?

Is this vulnerable to sql injection or not?

提问人:user3494179 提问时间:4/6/2022 最后编辑:JISTuser3494179 更新时间:2/8/2023 访问量:442

问:

在寻找解决问题的方法时,我发现了这个主题:

Rails 和 Arel 和 Scopes - 简化同一表/字段匹配上的多个 OR

其中一个答案是这样的:

fields = ["name", "email", "phone", "business_name", "doc_id"]
filter = fields.map { |field| "lower(#{field}) LIKE '#{term.downcase}'" }.join(' OR ')
@customers = Customer.where(filter)

但是,如果 term 是用户输入的参数,这是否容易受到 sql 注入的影响?如果是这样,我怎样才能让它成为 sql 注入证明?sanitize_sql_like能解决sql注入问题吗?所以像这样:

filter = fields.map { |field| "lower(#{field}) LIKE '#{sanitize_sql_like(term.downcase)}'" }.join(' OR ')
@customers = Customer.where(filter)

更新: 这是解决方案吗?

fields = ["name", "email", "phone", "business_name", "doc_id"]
filter = fields.map { |field| "lower(#{field}) LIKE :query" }.join(' OR ')
@customers = Customer.where(filter, query: "%#{sanitize_sql_like(term.downcase)}%")
Ruby-on-Rails ActiveRecord SQL 注入

评论


答:

3赞 max 4/6/2022 #1

将用户输入插入 SQL 字符串的任何情况都是潜在的 SQL 注入漏洞。转义输入(这是sanitize_sql_like所做的)可以在一定程度上缓解它,但最好使用绑定变量将用户输入单独传递到数据库,这样它就不会被误解为实际命令。

# bad
User.where("email like %#{params[:email]}%")

# good
User.where("email like ?", "%#{params[:email]}%")

您也不需要手动构造那个巨大的 SQL 字符串。相反,只需创建一个 ActiveRecord::Relations 数组,并使用该方法将它们连接在一起。.or

fields = %w{ name email phone business_name doc_id }
filters = fields.map do |field|
  Customer.where("lower(#{field}) LIKE :term", term: term.downcase)
end
@customers = filters.drop(1).inject(filters.first) do |memo, scope|
  memo.or(scope)
end 

这可能看起来有些令人生畏,但它实际上只是一个花哨的裤子版本:

Customer.where("lower(name) LIKE :term", term: term.downcase)
        .or(
          Customer.where("lower(email) LIKE :term", term: term.downcase)
        )
        .or(
          Customer.where("lower(phone) LIKE :term", term: term.downcase)
        )
        # ...

评论

0赞 max 4/6/2022
您可以像在链接的答案中一样使用 Arel 的 matches 方法,但请注意,除非您传递 Arel::Nodes::BindParam 或 Arel::Nodes::SqlLiteral,否则它实际上会将用户输入插入 SQL 查询
0赞 user3494179 4/7/2022
感谢您的回复。第一部分 (fields.map { |field| <query> } -> 每次迭代是产生 1 db 事务还是 1 个事务?第二部分(filters.drop(1).inject(filters.first)看起来并不直观。如果其他开发人员想要处理它,他或她可能不会立即理解它。它与filters.flatten.compact做同样的事情吗?
0赞 max 4/7/2022
是的,它创建单个 ActiveRecord 关系,从而创建单个查询。 并且用于展平嵌套数组并删除 NIL,因此它没有可比性。如果您担心维护问题,请注释代码...flattencompact
0赞 max 4/7/2022
inject实际上是一种循环方法,有点像每个方法,只是它为块的每次迭代生成修改后的备忘录。对于任何有经验的 Ruby 开发人员来说,这并不是什么陌生的事情。
0赞 TonyArra 4/14/2022
@max我相信您也可以通过使用 Customer.none 初始化 inject/reduce 在单个块中完成此操作