RailsCasts中文版,#25 SQL Injection 谨防SQL注入

RailsCasts中文版,#25 SQL Injection 谨防SQL注入_第1张图片

接下来的几篇,我们会讨论一些关于安全的话题,以免你的站点频频遭受黑客的攻击。其中第一个基本安全原则就是永远不要信赖来自用户的输入。在Rails中来讲就是说,从页面参数中传递进来的数据一定要小心对待。用户可以有意的设置参数值,甚至可以设置参数键,所以得谨慎的使用。这个原则对于cookie数据也同样适用。而会话数据中的内容是由我们的程序控制的,可以放心使用。

所有安全问题中,SQL注入是最最臭名昭著的了。当糟糕的代码实现将用户输入的SQL语句直接拼接在请求中提交至数据库,这悲剧的一切就发生了。通过一个例子来说明吧:

小例子:任务查询

在这个例子中,用户可以在输入框中写入查询条件,点击"Search"后系统会向数据库提交一个类似于SQL语句的查询并返回名称与输入匹配的所有任务。

RailsCasts中文版,#25 SQL Injection 谨防SQL注入_第2张图片

用户输入被直接传递到了向数据库发送的请求中,这样实现是有安全风险的。

class TasksController < ApplicationController
 def index
  @tasks = Task.find(:all,:conditions=>"name LIKE ’%#{params[:query]}%’")
 end
end

TasksController 代码展示了一个不安全的SQL查询。

问题在于,如果用户在输入框中不是简单的输入一个任务的名称,而是一个单引号后面再随便写点什么比如他输入Task1 'test。查询下发后就会报错了。

Processing TasksController#index (for 127.0.0.1 at 2009-02-01 21:29:26) [GET]
  Parameters: {"query"=>"Task 1’TEST"}
  Task Load (0.0ms)   SQLite3::SQLException: near "TEST": syntax error: SELECT * FROM "tasks" WHERE (name like ’%Task 1’TEST%’) 

ActiveRecord::StatementInvalid (SQLite3::SQLException: near "TEST": syntax error: SELECT * FROM "tasks" WHERE (name like ’%Task 1’TEST%’) ):

如果用户输入单引号会导致SQL语句生成错误。

我们代码中的SQL语句中本来期待用户输入一个查询条件,可是没想到他却输入了一个单引号,一下子将SQL语句给结束了。单引号后面的部分又成了一个不完整的SQL语句,执行起来肯定就要报错了。如何做能避免这种情况发生呢?

解决方法是避免让查询条件中出现本应我们自己控制的单引号。Rails为我们提供了方便的手段完成这个工作。将SQL语句中的参数用一个?代替后放在一个数组的第一个元素。数组中后面的元素依次替换SQL语句中站位的?的值。如果语句中有三个地方需要通过参数传入,那么我们需要创建一个长度是四的数组,第二个到第四个元素分别代表三个参数。

@tasks = Task.find(:all, :conditions=> [ "name LIKE ?", "%#{params[:query]}%" ]

这是更好更安全的实现方法。

接下来如果在界面中输入带有单引号的查询条件,Rails会将单引号转义为普通的字符,而不是被认为SQL格式中的一部分。还差一点儿,咱们希望进行的是LIKE操作的模糊查询,需要在查询条件两边带上百分号。需要注意的是,百分号需要添加在参数数组的元素两边而不是在SQL语句的问号两边。日志中可以看到查询是正确的。

Processing TasksController#index (for 127.0.0.1 at 2009-02-01 21:59:31) [GET]
  Parameters: {"query"=>"Task 1’TEST"}
  Task Load (0.5ms)   SELECT * FROM "tasks" WHERE (name like ’%Task 1’’TEST%’)

这样就完美解决了单引号的问题。

Note that as we’re using Sqlite the quote is escaped by using two single quotes. Other databases may use a backslash before the quote.

只有在使用find方法中使用:conditions参数进行查询是才需要考虑避免SQL注入。如果你使用的是更新的查询方法find_by可以参考《RailsCasts中文版,#2 Dynamic find_by Methods 使用动态的find_by方法进行查找操作》 Rails会自动处理这些问题防止SQL注入发生。


作者授权:You are welcome to post the translated text on your blog as well if the episode is free(not Pro). I just ask that you post a link back to the original episode on railscasts.com.

原文链接:http://railscasts.com/episodes/25-sql-injection


你可能感兴趣的:(sql注入,Rails,railscasts)