ruby-on-rails – 使用Postgres强制转换将动态列名称安全地传递给ActiveRecord查询?

前端之家收集整理的这篇文章主要介绍了ruby-on-rails – 使用Postgres强制转换将动态列名称安全地传递给ActiveRecord查询?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我在我的应用程序中没有日期查询,我做了很多时间,我想抽出一些问题.

所以说我有一个带有DateTime starts_at字段的模型:

Shift.where('starts_at::time > ?','20:31:00.00')
-> SELECT "shifts".* FROM "shifts" WHERE (starts_at::time > '20:31:00.00')

这正确地返回所有大于20:31的’starts_at’值.

我想动态地将列名传递给查询,所以我可以这样做:

Shift.where('? > ?',"#{column_name}::time",'20:31:00.00').
-> SELECT "shifts".* FROM "shifts" WHERE ('starts_at::time' > '20:31:00.00')

在此示例中,这不起作用,因为搜索将starts_at :: time作为字符串执行,而不是作为具有时间转换的列.

如何安全地将column_name传入带有:: time cast的查询?虽然这不会接受用户输入,但我仍然希望确保考虑sql注入.

解决方法

这比你最初想象的要复杂得多,因为标识符(列名,表名,…)和值(‘pancakes’,6,…)是sql中具有不同引用规则甚至引用的非常不同的东西字符串(字符串的单引号,标准sql中标识符的双引号,MysqL中标识符的反引号,sql-Server中标识符的括号,……).如果您考虑像Ruby变量名称这样的标识符,以及像文字Ruby值这样的值,那么您就可以开始看到差异.

当你这样说时:

where('? > ?',...)

两个占位符都将被视为值(不是标识符)并被引用.为什么是这样? ActiveRecord无法知道哪个?应该是一个标识符(例如created_at列名),它应该是一个值(例如20:31:00.00).

数据库连接确实有一个专门用于引用列名的方法

> puts ActiveRecord::Base.connection.quote_column_name('pancakes')
"pancakes"
=> nil

所以你可以这样说:

quoted_column = Shift.connection.quote_column_name(column_name)
Shift.where("#{quoted_name}::time > ?",'20:31:00.00')

这有点令人不愉快,因为我们在使用字符串插值来构建sql时会退缩(或至少我们应该).但是,quote_column_name将处理column_name中任何狡猾或不安全的事情,因此这实际上并不危险.

你也可以说:

quoted_column = "#{Shift.connection.quote_column_name(column_name)}::time"
Shift.where("#{quoted_name} > ?",'20:31:00.00')

如果您并不总是需要将列名转换为时间.甚至:

clause = "#{Shift.connection.quote_column_name(column_name)}::time > ?"
Shift.where(clause,'20:31:00.00')

你也可以使用extract or one of the other date/time functions而不是类型转换,但你仍然会留下引用问题和一些有点引人注目的quote_column_name调用.

另一种选择是将column_name列入白名单,以便只允许特定的有效值.然后,您可以将安全column_name直接放入查询中:

if(!in_the_whitelist(column_name))
  # Throw a tantrum,hissy fit,or complain in your preferred fashion
end
Shift.where("#{column_name} > ?",'20:31:00.00')

这应该没问题,只要你没有任何时髦的专栏名称,如“必须吃早餐”或类似的东西,总是需要正确引用.您甚至可以使用Shift.column_names或Shift.columns来构建白名单.

使用白名单和quote_column_name可能是最安全的,但quote_column_name方法应该足够了.

猜你在找的Ruby相关文章