动态记录中的动态where子句避免sql注入

时间:2018-03-14 13:24:14

标签: ruby-on-rails ruby activerecord sql-injection

你知道如何构建一个动态查询来避免sql注入 ?

property = 'foo'
value = 'bar'
SomeObject.where("#{property} > ?", value)
# works but permit sql inj
SomeObject.where(":property > :value", property: property, value: value)
# create select * from some_object where 'foo' > 'bar'
# and the 'foo' I need without the quotes

4 个答案:

答案 0 :(得分:3)

SomeObject.where(
  "#{SomeObject.connection.quote_column_name(property)} > :value", 
  value: value
)

<强>更新

示例1(尝试结束语句并注入新语句):

property = '; DROP TABLE users; --'
User.where("#{User.connection.quote_column_name(property)} > :value", value: 3)
# => SELECT "users".* FROM "users" WHERE ("; DROP TABLE users; --" > 3)

示例2(尝试结束列名称引用):

property = '"; DELETE FROM users;--'
User.where("#{User.connection.quote_column_name(property)} > :value", value: 3)
# => SELECT "users".* FROM "users" WHERE ("""; DELETE FROM users;--" > 3)

答案 1 :(得分:2)

不确定您的用例,arel可以帮助您解决此问题

some_object_table = SomeObject.arel_table
SomeObject.where(some_object_table[property.intern].gt(value))

这将适当地执行查询以及您已经爱上rails的所有转义。

这是有效的,因为arel是rails使用的基础查询汇编程序ActiveRecord,其中子句可以理解Arel::Nodes而没有问题(实际上它们是如何组合开始的)

另外,考虑到动态特性,您可能需要检查property是否为有效列以避免SQL级别错误,例如

raise AgrumentError unless some_object_table.engine.columns.map {|c| c.name.intern}.include?(property.intern)
# or 
raise AgrumentError unless SomeObject.column_names.map(&:to_sym).include?(property.to_sym)

答案 2 :(得分:1)

一种简单但安全的方法是将允许的属性名称列入白名单:

PROPERTIES = ["foo", "bar", "baz"].freeze
def find_greater_than(property, value)
  raise "'#{property}' is not a valid property, only #{PROPERTIES.join(", ")} are allowed!" if !PROPERTIES.include?(property)
  SomeObject.where("#{property} > ?", value)
end

您可以(如@engineersmnky指出的那样)动态检查可用列:

raise "Some Message" if SomeObject.column_names.include?(property)

但我不喜欢这种方法,因为可搜索的列应该是一个决定,而不是自动化。

另一个方法是使用Rails提供的清理。

def find_greater_than(property, value)
  sanitized_property = ActiveRecord::Base.connection.quote_column_name(property)
  SomeObject.where("#{sanitized_property} > ?", value)
end

引用逻辑由DB特定的连接适配器实现。

答案 3 :(得分:0)

就我而言,我以使用结束

ActiveRecord::Base.connection.quote_table_name 函数在查询前清理列名。

property = 'foo'
value = 'bar'

sanitized_property = ActiveRecord::Base.connection.quote_table_name(property)
SomeObject.where("#{sanitized_property} > ?", value)

该函数可以处理表名和列名定义。

property = 'table.column'

会产生

where `table`.`column` > `bar`
相关问题