你知道如何构建一个动态查询来避免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
答案 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`