ruby和rails的安全性问题学习

ruby和rails的安全性问题学习

摘自:http://blog.csdn.net/yangbo_hr/archive/2008/01/01/2008183.aspx

 因为在论坛http://www.ruby-lang.org.cn/上有drive2me兄问php?name=rails" onclick="tagshow(event)" class="t_tag">rails的安全性如何,而我也不是很了解,所以
在网络上学习了一下,下面就是一些总结,一来是帮助drive2me兄,回答他的问题,二来也是备忘,希望有更多人研究
rails的安全性,写出更安全的webapp。

1. ruby的安全机制
 参考《programming ruby》中的Locking ruby in safe一章。
 主要说明了安全级别和脏对象。

 脏对象(tainted object):所有外部数据都是危险的,比如对表单提交的数据进行eval操作就会造成很严重的安全问题。

 所有从外部进入的ruby解释器的数据都可以被标记为脏对象(tainted),
 当ruby解释器运行在某一个安全模式下时,有危险的方法调用将导致系统抛出SecurityException.

 变量$SAFE决定ruby的“怀疑级别”。数字越低越不安全。
 $SAFE=0 让ruby不检查外部提供的脏(tainted)数据的使用情况,这是ruby的默认模式。
 $SAFE>=1 让ruby不允许脏(tainted)数据使用可能有危险的方法,比如eval。
 $SAFE>=2 让ruby不允许从全局可写位置装载程序。
 $SAFE>=3 让ruby把所有新创建的对象都认为是脏的(tainted).并且不能把一个脏对象untaint.
 $SAFE>=4 让ruby有效地把正在运行的程序划分成两部分,脏的和干净的。

 $SAFE变量一旦设置了一个值,就不可能再设置为更低的值了。
 $SAFE值是线程独立的,新线程会继承当前的$SAFE值,但是新线程可以修改这个值,而其它线程不受影响。
 使用这个机制可以实现一个沙盒,用来运行外部的程序。例如:
  f=open(filename,"w")
  f.print ... # write untrusted program into file.
  f.close
  Thread.start do
  $SAFE = 4
  load(filename, true) # wrapp 到一个匿名模块中去。
  end

 创建一个Proc对象时,实际的安全级别被存储在该对象中。如果这个Proc对象是脏的(tainted), 并且
 当前的安全级别大于这个Proc对象的实际安全级别,那么,这个Proc对象是不能被传递给其它方法去使用的。

 脏对象:ruby解释器自动地把任何从外部得到的对象都标记为脏对象(tainted object).比如从环境变量中得到
 的string对象,从文件中读取的string对象等。
 你的程序从脏对象得到的新对象也会被标记为脏对象,比如:
  y1 = ENV["HOME"]
  y1.tainted?  # => true
 
  y2 = y1[2, 4]
  y2.tainted?  # => true


2. webapp共有的安全问题

 = SQL Injection (SQL注入):
  问题:因为不对来自于外部的数据按照sql的元字符进行转义,比如不对"\","'"等字符进行sql转义,从而
   使得黑客可以改变WHERE子句条件来增删改数据,甚至是执行任意的sql语句。
  如何避免:rails自动处理这些需要转义的字符。如果你自己写了condition, limits, order 那么你必须
  正确转义这些危险的字符。
  例子:
  错误的用法: Email.find_all "owner_id = 123 AND subject = '#{@params['subject']}'"
  攻击方法: 提供subject的内容为" 'or 1 --' ".
  解决办法: subject = @params['subject']
     Email.find_all [ "owner_id = 123 AND subject = ?", subject ]
     或者: Email.find_by_sql "SELECT * FROM email WHERE owner_id = 123 AND subject = #{Email.quote(subject)}"

 = Cross Site Scripting (CSS/XSS) (跨站脚本) :
  浏览器的用户使用cookie来跟踪session,CSS是一种盗取cookie,最后盗取session登录权限的技术。
  cookie只能被创建它的domain访问到。最简单的办法就是放一段恶意javascript在目标网站(要盗取用户权限的网站)
  中,当用户访问目标网站时,打开的页面中会执行恶意javascript代码,因为来自于目标网站,所以恶意代码可以访问
  用户的当前cookie,于是把这些数据放入url参数中传递到攻击者的网站去。
  例子:
  错误的写法:<%= @params['text'] %>
  攻击方法:提交的参数写为 <script>alert(document.cookie)</script>,让受害者打开这个目标网站的受攻击
  页面,从而把受害者的cookie传到攻击者的网站去。
  解决办法:把每个会在页面中显示的语句都进行html转义。用<%=h @params['text'] %>

  用echo服务造成的CSS攻击:因为echo服务会把收到的内容原封不动地返回去,所以攻击者会让受害者打开一个含有
  向目标网站发送带有传递cookie信息的javascript脚本,当浏览器收到echo服务返回的内容时,有些IE会渲染这
  个页面,并执行其中的javascript代码,从而把cookie发送到攻击者的网站去。用于攻击的页面内容如下:
  <form action="http://target.domain:7/" method="post">
   <input type="hidden" name="code" value="some_javascript_code_here" />
   <input type="submit" />
  </form>
  解决办法:关闭目标服务器上可能造成CSS攻击的echo服务和其它服务(FTP,POP3等)。

  有专门的网站记录CSS攻击事件:http://www.xssed.com/

 = Session hijacking (session 劫持):
 攻击者通过 sniffing 不安全网络,获取别人的cookie.然后使用该cookie登录目标网站。

  解决办法:
  *告诉浏览器传送cookie时使用ssl.
  ActionController::Base.session_options[:session_secure] = true
  *登出后销毁session: session[:user_id] = nil
  *登录成功后创建并复制一个新的session.需要复制旧session的所有对象。 用reset_session方法实现。

 = Session fixation (session 固化):
 攻击者设法让受害者使用攻击者已知的一个session identifier(session标识)来进行登录。
 第一步,攻击者创建一个有效的session identifier,rails不接受任意形式的session identifier,而php接受,
 所以攻击者必须要访问目标网站才能攻取到rails产生的session identifier,从这点看rails比php安全。
 第二步,攻击者设法让受害者的浏览器使用这个session identifier去登录,这被称作"真实session固化".
 真实session固化的一个方法是把受害者的网络请求中途截断,然后改写目标网站返回的session identifier.
 等受害者登录网站后,攻击者就可以在受害者退出前使用受害者的登录帐号了。
 
 解决办法:
 *不在公共页面上创建有效的session. 可以使用controller的session :off方法关闭所有method的session产生。
 *在成功登录后,创建一个新的session. reset_session.
 *有效地设置cookie的过期时间。

 = Cross-site Request Forgery (跨站请求伪造)
 攻击者设法让受害者打开一个网页,或者打开一封邮件,其中有图片链接,这个链接会让用户向他已经登录的网站发起一个
 请求,来做一些受害者不知道的事情,比如修改密码、下订单、删除数据等等。攻击用的url类似:
 <img src="http://www.application.com/order/20/delete" />

 解决办法:
 *正确使用GET/POST方法,GET读取数据,POST修改数据。
 *使用CSRF-KILLER插件为请求加上token(令牌).


3. rails自身使用不当造成的安全隐患(mass-assignment problem)
 = 直接用表单创建记录, 也被称作 mass-assignment 问题
 普通的写法:
  <form method="post" action="http://website.domain/user/register">
  <input type="text" name="user[name]" />
  <input type="text" name="user[password]" />
  </form>
  User.create(@params['user'])
 恶意代码:
  <form method="post" action="http://website.domain/user/register">
  <input type="text" name="user[name]" />
  <input type="text" name="user[password]" />
  <input type="text" name="user[role]" value="Admin" />
  <input type="text" name="user[approved]" value="1" />
  </form>
 解决办法:
  attr_protected, 加手工赋值, user.approved = sanitize_properly(@params['user']['approved'])
  attr_accessible :name, :password, 让rails默认情况下不让任何属性能够"mass-assignment"除非
  显式地说明该属性可以批量访问(mass-assignment)。

 = rails自身的安全问题:
 http://blog.evanweaver.com/artic ... tack-against-1-1-4/

4. help tools
 safeERB
 csrf-killer

5. 我的结论
 rails提供了很多安全方面的功能,所以开发一个安全的webapp用rails会比其它webapp框架容易。
 webapp面临的大都数安全问题是共有的,并非应用rails而造成的,反而rails提供的很多安全化手段可以让webapp变得
 更加安全。

 当然rails和ruby程序自身也有一些安全隐患,但并不是致命和不可克服的,需要紧跟rails和ruby官方网站发布的安全
 声明。

参考资料:
 <<programming ruby 2nd>> (Table 25.1. Definition of the safe levels)
 http://www.rorsecurity.info/ruby-on-rails-security-cheatsheet/
 http://ianloic.com/insecurity_is_ruby_on_rails_best_practice
 http://www.lonerunners.net/blog/ ... eb-application.html
 http://sonjayatandon.com/05-2006 ... with-ruby-on-rails/
 http://manuals.rubyonrails.com/read/chapter/47
 http://erlend.oftedal.no/blog/?blogid=20
 http://manuals.rubyonrails.com/read/book/8
 http://www.slideshare.net/amiable_indian/ruby-on-rails-security

作者简介:
 杨波, 技术经理, 结信网络有限公司,ruby和rails的爱好者,SCJP,工作中使用java开发。
 研究兴趣:Search Engine,AI,自然语言处理,ruby,rails,OpenGL,OSGi

感谢lz的付出让我们每个读者收获颇丰。
哇,没想到我的一个问题,Bob回答的这么认真和仔细,好好的学了一回。尤其是那些解决方法,这些都是我们开发者最关心的。收益匪浅!

谢谢Bob的指导!