Rails 5 SQL注入
我已经在一段时间内阅读了关于各种SO线程,指南等的内容……但所有答案都是相互矛盾和矛盾的。
似乎有许多类似的方法,很多答案都说使用不同的方法。
sanitize
-
sanitize_conditions
-
sanitize_sql
-
sanitize_sql_array
-
sanitize_sql_for_assignment
-
sanitize_sql_for_conditions
-
sanitize_sql_hash
-
sanitize_sql_hash_for_assignment
-
sanitize_sql_hash_for_conditions
-
sanitize_sql_like
我正在尝试编写一个“原始查询”适配器,它允许我运行原始的Postgres查询,但允许我插入来自危险用户输入的我自己的参数。
我不能在这几个实例中使用AR,因为我正在进行复杂的纬度/经度计算,聚合函数,复杂子查询等。
到目前为止,我尝试了两种方法:
方法1
对于这种方法,我不知道sanitize
是否是上述的最佳选择,或者它是否适用于100%的情况……(我只使用Postgres)
class RawQuery def exec(prepared, *params) prepared = query.dup params.flatten.each_with_index do |p, i| prepared.gsub!("$#{i + 1}", ActiveRecord::Base.sanitize(p)) end ActiveRecord::Base.connection.exec_query(prepared) end end
琐碎的用法示例(当然通常不会这么简单,或者我只会使用AR):
RawQuery.new.exec('SELECT * FROM users WHERE name = $1', params[:name])
此外,似乎sanitize
代表quote
。 但根据这篇SOpost说它只是用单引号包装东西是不安全的…所以我不知道。
方法2
我不确定这是否同样安全,但它似乎使用了一个实际的PG准备function(我认为它是100%安全的)。 唯一的问题是rails不会将其打印到控制台,也不包括SQL执行时间(这会破坏我的分析工具)。
class RawQuery def prepare(query, *params) name = "raw_query_#{SecureRandom.uuid.gsub('-', '')}" connection = ActiveRecord::Base.connection.raw_connection connection.prepare(name, query) connection.exec_prepared(name, params) end end
使用方式相同:
RawQuery.new.prepare('SELECT * FROM users WHERE name = $1', params[:name])
一种方法比另一种更安全吗? 100%安全吗?
我的应用程序总是远远超出了Rails的SQL能力范围,我需要一个好的库,我可以包含在我认为完全安全的所有项目中。
使用quote
是安全的。 我在你链接的页面上阅读了答案,我没有看到有人说这quote
是不安全的。 我看到你关于使用“引号”的问题。 是的,如果您只是在字符串周围加上引号,那就是不安全,例如:
q = "SELECT * FROM users where email = '#{params[:email]}'"
但是使用quote
(方法)很好:
q = "SELECT * FROM users where email = #{connection.quote(params[:email])}"
你可以在控制台中玩游戏并尽力打破它,但我认为你不能:
2.3.3 :003 > ActiveRecord::Base.connection.quote("f''oo") => "'f''''oo'"
如果你成功了,我肯定Rails团队想知道(私下)! 但正如您所看到的, quote
方法不仅仅是在开头和结尾都贴上引号。
此外,由于您说您正在寻找权威引文,因此源代码本身中的注释表明引用用户输入是这些函数的预期目的:
# Quotes the column value to help prevent # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection]. def quote(value)
# Quotes strings for use in SQL input. def quote_string(s) #:nodoc:
(注意我为评论显示quote_string
,但你应该使用quote
,它试图找出数据类型并做一些合适的事情。)
顺便说一句,这里有一个与你类似的问题,2014年我的回答,以及一些替代方案: 如何在rails中执行带有动态绑定的原始更新sql