ruby-on-rails – 使用Postgres强制转换将动态列名称安全地传递
我在我的应用程序中没有日期查询,我做了很多时间,我想抽出一些问题.
所以说我有一个带有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') 你也可以使用 另一种选择是将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方法应该足够了. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |