Ruby on Rails SQL 注入 - 构建查询

Ruby on Rails SQL Injection - Building a query

提问人:Victor Policastro 提问时间:12/5/2018 更新时间:12/5/2018 访问量:810

问:

我正在解决系统中的所有SQL注入,我发现了一些我不知道如何处理的问题。

有人可以帮我吗?

这是我的方法

def get_structure()
   #build query
   sql = %(
           SELECT pc.id AS "product_id", pc.code AS "code", pc.description AS "description", pc.family AS "family", 
                  p.code AS "father_code", p.description AS "father_description", 
                  p.family AS "father_family"
           FROM products pc
           LEFT JOIN imported_structures imp ON pc.id = imp.product_id
           LEFT JOIN products p ON imp.product_father_id = p.id
           WHERE pc.enable = true AND p.enable = true
   )
   #verify if there is any filter
   if !params[:code].blank?
     sql = sql + " AND UPPER(pc.code) LIKE '%#{params[:code].upcase}%'"
   end
   #many other parameters like the one above
   #execute query
   str = ProductStructure.find_by_sql(sql)
end

谢谢!

Ruby-on-Rails Ruby SQL注入

评论

0赞 3limin4t0r 12/5/2018
这不会解决您的问题,但确实使事情更具可读性。当有大块文本时,请考虑使用 heredoc。某些 IDE 还会根据提供的标记调整语法突出显示。在这种情况下,或者如果您不希望进行字符串插值。<<~SQL<<~'SQL'

答:

1赞 tadman 12/5/2018 #1

您需要将其转换为占位符值 () 并将数据添加为单独的参数。find_by_sql可以接受数组:?

def get_structure
   #build query
   sql = %(SELECT...)
   query = [ sql ]

   if !params[:code].blank?
     sql << " AND UPPER(pc.code) LIKE ?"
     query << "%#{params[:code].upcase}%"
   end

   str = ProductStructure.find_by_sql(query)
end

请注意,最好在 String 上使用,因为它可以避免复制。<<+=

4赞 engineersmnky 12/5/2018 #2

你可以使用 Arel 来为你转义,它是 ActiveRecord/Rails 的底层查询构建器。例如。

products = Arel::Table.new("products")
products2 = Arel::Table.new("products", as: 'p')
imported_structs = Arel::Table.new("imported_structures")
query = products.project(
  products[:id].as('product_id'),
  products[:code],
  products[:description], 
  products[:family], 
  products2[:code].as('father_code'),
  products2[:description].as('father_description'),
  products2[:family].as('father_family')).
  join(imported_structs,Arel::Nodes::OuterJoin).
    on(imported_structs[:product_id].eq(products[:id])).
  join(products2,Arel::Nodes::OuterJoin).
    on(products2[:id].eq(imported_structs[:product_father_id])).
  where(products[:enable].eq(true).and(products2[:enable].eq(true)))
if !params[:code].blank?
  query.where(
     Arel::Nodes::NamedFunction.new('UPPER',[products[:code]])
       .matches("%#{params[:code].to_s.upcase}%")
  )
end

SQL 结果:(使用params[:code] = "' OR 1=1 --test")

SELECT 
  [products].[id] AS product_id, 
  [products].[code], 
  [products].[description], 
  [products].[family], 
  [p].[code] AS father_code, 
  [p].[description] AS father_description, 
  [p].[family] AS father_family 
FROM 
  [products] 
  LEFT OUTER JOIN [imported_structures] ON [imported_structures].[product_id] = [products].[id] 
  LEFT OUTER JOIN [products] [p] ON [p].[id] = [imported_structures].[product_father_id] 
WHERE 
  [products].[enable] = true AND 
  [p].[enable] = true  AND 
  UPPER([products].[code]) LIKE N'%'' OR 1=1 --test%'

要使用

ProductStructure.find_by_sql(query.to_sql)

我更喜欢 ,如果可用,而不是查询,因为:ArelString

  • 它支持转义
  • 它利用您现有的 Sytnax 连接适配器(因此,如果您更改数据库,它是可移植的)
  • 它是用代码构建的,因此语句顺序无关紧要
  • 它更具动态性和可维护性
  • 它由ActiveRecord原生支持
  • 您可以构建任何您能想象到的复杂查询(包括复杂的联接、CTE 等)
  • 它仍然非常可读

评论

0赞 3limin4t0r 12/5/2018
我有几个问题/意见。1) 你不需要将结果(在 if 语句中)保存回变量中吗?2) 我会通过简单地调用来实例化 arel 表,如果您需要别名。3)我会使用。query.where(...)queryModel.arel_tableModel.arel_table.alias('name').outer_join(arel_table).join(arel_table, Arel::Nodes::OuterJoin)
1赞 engineersmnky 12/5/2018
@JohanWentholt 1) 否。阿雷尔的地方正在变异。2) 提供的代码可以带或不带导轨使用(仅限) 3) 是的,你可以这样做Arel