且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Rails开发细节《八》Rails应用的安全

更新时间:2022-09-02 11:06:36

看过本文之后,你应该熟悉下面的内容:

  • 所有显著的对策。
  • rails中session的概念,其中存放的内容,常见的攻击手段。
  • rails中大量的分配问题。
  • 在提供管理接口的时候,你不得不关注的一些事情。
  • 如何管理用户:登陆,退出,在各个层面的攻击方法。
  • 常见的注入攻击方法。


1.简介
web框架帮助开发者构建web应用。它们中的一些还可以帮助你构建安全的web应用。事实上一个框架并不比两一个框架安全,如果你正确的使用,你可以用很多框架构建安全的web应用。rails包含一些聪明的帮助工具,例如防止注入攻击的工具,因此这几乎不是什么问题。高兴的是,我审计过的很多rails应用都有一个很好的安全级别。

通常来说,没有即插即用的安全。安全依赖于使用框架的人,有时候是开发的方法。它依赖于web应用环境的所有层:后端存储,web服务器,web应用本身(甚至可能是其它层或者其他应用)。


Gartner Group估计大约75%的攻击发生在web应用层,发现在审计的300个站点中,97%是容易受到攻击的。这是因为相对来说,web应用更容易攻击,因为它们容易理解和操作,设置非专业人士都可以理解和操作。

web应用的威胁包括用户账户劫持,绕开访问控制,读写敏感数据,显示欺骗信息。甚至一个攻击者还可能会安装木马程序,或者是一个主动的电子邮件发送软件,目标是金融数据,或者是通过修改公司的资源来破坏品牌。为了阻止攻击,最小化攻击的影响,移除攻击点,首先要做的是,你需要完全理解攻击的方法,才可以找到正确的对策。这就是本文的目的。

为了开发安全的web应用,你不得不保持各层的更新,并且了解你的敌人。订阅安全邮件列表,保持最新的信息,阅读安全类的博客。我是通过手动完成的,因为那就是你如何发现下流的逻辑安全问题。

2.sessions
开始了解安全的一个好地方就是session,很容易受到特殊攻击的地方。

2.1.什么是session
http是一个无状态的协议,session使得它有了状态。

大多数应用需要跟踪特定用户的状态。这可能是购物车中的内容,或者是一个登陆用户的userid。如果没有session,用户需要在每次请求进行识别和验证。在新用户访问应用的时候,rails会自定创建新的session。如果用户已经使用了应用,会加载一个已经存在的session。

session由一个哈希值和一个sessionid组成,sessionid通常是一个32个字符的哈希字符串。每个发送到客户端浏览器的cookie都包含sessionid。浏览器在客户端的每个请求中会将sessionid发送回来。在rails中你可以通过下面的代码保存和获取session值。


  1. session[:user_id] = @current_user.id 
  2. User.find(session[:user_id]) 



2.2.session id

sessionid是一个32字节的md5哈希值。

sessionid由随机字符串的哈希值组成。随机字符串包括当前时间,0-1之间的随机数,ruby解释器的进程id(也是一个随机数字),一个常量字符串。当目前为止,暴利攻击rails的sessionid是不可行的。MD5是不可逆的,但是可以碰撞,因此创建一个相同哈希值的字符串只是存在理论的可能性。

2.3session劫持
偷了一个用户的sessionid之后,一个攻击者就可以用受害者的名义访问web应用。

很多web应用都有验证系统:用户提供登陆账号和登录密码,web应用验证这些信息,验证通过之后,将用户的id保存到session中。在这之后,这个session就是有效的了。每次请求都会加载用户,通过session中的用户id识别用户,不需要再次验证。在cookie的sessionid用来识别session。

因此,cookie为web应用提供了临时的验证功能。每个从某个人哪里获取cookie的用户,可以以那个人的名义使用web应用,可能会产生严重的后果。下面是一些劫持session的方法,已经相应的对策。

在不安全的网络中嗅探cookie。无线局域网就是这样一个网络环境。在一个非加密的无线局域网,尤其容易监听所有连接客户端的通信。这就是为什么不要再咖啡店工作的原因。web应用的构建者应该通过SSL来提供一个安全的连接。在Rails3.1以及后续版本,可以在配置文件中进行配置,来强迫在应用中使用SSL连接。


  1. config.force_ssl = true 



大多数的人在公共终端工作之后,没有清除cookie。如果刚才的用户没有退出web应用,你就可以以刚才那个用户的身份使用web应用。在web应用中给用户提供一个log-out按钮,并且放在显著的位置。

很多跨站点攻击脚本cross-site scripting(XSS)的目标就是获取用户的cookie。

不是偷一个对攻击者来说未知的cookie,而是将用户的session的身份(在cookie中)修改为他可以认识的。在后面会讲到这个所谓的session固定。

大多数攻击者的主要目的是获取金钱。偷一个银行账号的私下价格是10-1000美元,获取信用卡号是0.4-20美元,拍卖网站的账户是1-8美元,电子邮件密码是4-30美元。

2.4.session指南

下面是一些常用的session指南

不要再session中存储大对象。将内容存储到数据库,在session中只保存数据的id。这可以消除同步这个令人头痛的问题,还不会占满session的存储空间(这依赖于你选择存储session的类型)。服务端的session存储空间你可以清除session,但是很难减轻客户端的存储空间压力。

关键的数据不应该存储在session中。如果用户清除了他的cookie,或者是关闭了浏览器,这些信息就会消失。在客户端的session存储,用户可以读取数据。

2.5.session存储
rails提供了若干session存储机制。最重要是ActiveRecord::SessionStore和ActionDispath::Session::CookieStore。

有很多的session存储,rails存储session的哈希值和sessionid。大多数实时应用选择ActiveRecord::SessinStore(或者是它的派生类),通过文件存储,由于性能和维护的原因。ActiveRecord::SessionStore将哈希值和session id存储在数据库表中,在每次请求都会保存和获取哈希值。

Rails2引入了一种新的默认session存储方式,CookieStore。将session哈希值直接存储在客户端的cookie中。服务端直接从cookie中获取session哈希值,消除了对于session id的需要。极大地增加了应用的速度,但是这是个有争议的存储方式,你不得不考虑隐藏的安全问题:

cookie的大小严格限制为4KB。这还好,之前讲过,你不应该在session中存储大数据。在session存储当前用户的数据库id通常是可以的。

客户端可以看到在session中存储的所有内容,因为是明文存储(事实上是base64编码,但是没有加密)。当然,你不会在里面存储任何秘密。为了防止session哈希值被篡改,从session中用服务端的密钥计算的一个数字会被插入到cookie的最后面。

这就意味着这种储存方式依赖于这个密钥(和这个数字算法,默认是SHA512,还没有被破解)。因此不使用不重要的密钥,例如字典中的一个单词,或者是短于30个字符。把这个密钥放在你的environment.rb中。


  1. config.action_dispatch.session = { 
  2.   :key    => '_app_session'
  3.   :secret => '0x0dkfj3927dkc7djdh36rkckdfzsg...' 



因为CookieStore的继承类加密session哈希值,因此客户端是看不到的。

2.6.重复CookieStore session的攻击
你需要注意的另一种攻击是,在使用CookieStore的时候,受到的重复攻击。

攻击的过程是这样的:

一个用户收到信用卡信息,金额存储在session中(这不是一个好主意,我们只是来证明)。
这个用户买了一些东西。

 

2.7.固定session

 

1.首先,攻击者会访问目标网站。访问之后会产生一个session,这个session是合法的。

2.攻击者想办法让目标用户使用这个合法的session。使用下面的代码就可以修改页面的session。

 


  1. <script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
</script> 

3.攻击者引诱目标用户访问受过污染的页面,访问受污染的页面之后,目标用户就开始使用前面的合法的session。

4.目标用户使用攻击者的session在目标网站登录验证。

5.攻击者和目标用户共享一个session,攻击者就可以获取用户信息等其他信息。目标用户完全不知情。

 

2.8.固定session的对策

 

Rails开发细节《八》Rails应用的安全

最有效的办法就是在登录之后,声明旧的session失效。这样,攻击者就不能使用自己的哪个session或者目标用户的信息了,因为登录之后,重新分配了session。

还有一个办法,就是记录每次请求的其他信息,例如ip,浏览器类型等,每次请求都去验证当前请求和上次请求的这些信息是否匹配,来决定是否拒绝提供服务。

 

2.9.session过期

不仅在cookies中声明session的过期时间,而且在服务端也同时检查session是否过期。

 

3.CSRF跨站请求伪造

 

Rails开发细节《八》Rails应用的安全

1.受害者浏览一篇博客,博客是攻击者准备好的,博客中有一张图片,图片的地址是


  1. <img src="http://www.webapp.com/project/1/destroy"> 

2.我们知道,一张图片会产生一个请求,请求的地址就是img的src的内容。

3.受害者在webapp.com上的session还没有过期,因为他刚才还在浏览webapp.com,而且还没有退出。

4.受害者的浏览器发出请求http://www.webapp.com/project/1/destroy,删除id为1的project。

5.webapp.com验证受害者的session合法,然后执行这个请求,这个project被删除。请求的结果对于img来说是非预期结果,所以不会在网页上有任何显示。

6.受害者没有意识到任何事情,但是他再webapp.com上的project已经被删除了。

 

3.1.CSRF对策

首先,要正确使用get和post。

其次,在非get请求中增加token验证来保护你的应用免受csrf攻击。

post请求可以用代码发出。

下面就是一个例子。

 


  1. <a href="http://www.harmless.com/" onclick=" 
  2.   var f = document.createElement('form'); 
  3.   f.style.display = 'none'
  4.   this.parentNode.appendChild(f); 
  5.   f.method = 'POST'
  6.   f.action = 'http://www.example.com/account/destroy'
  7.   f.submit(); 
  8.   return false;">To the harmless survey</a> 

 

在a标签中内嵌了一个form,发送post请求,对数据造成破坏。

攻击代码也可以放在img的mouse事件中。

 


  1. <img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." /> 

一个应对csrf的办法就是在非get请求中加入token验证,在rails中已经内置了这种方法,只需要在applicationcontroller中加入下面一行代码。默认是已经加好的。

 


  1. protect_from_forgery :secret => "123456789012345678901234567890..." 

 

4.跳转和文件

在web应用中,还有一些安全漏洞是与跳转和文件相关的。

 

4.1.跳转

任何使用使用url传递的参数引导用户进行跳转,都可能是一个漏洞。

大部分的攻击都是把受害者引入一个假冒的网站,看起来和刚才的网站很相似。这就是所谓的钓鱼攻击

 

对付这类攻击的简单办法就是为跳转参数设立白名单,然后检查参数值是否在白名单中,再决定是否跳转。

 

4.2.文件上传

很多应用都允许用户上传文件,用户可以***设置文件的名称,这样攻击就可能会使用恶意的文件替代服务器中的文件。你允许用户上传文件到/var/www/uploads,可是用户写了一个文件名../../../etc/passwd,这样就覆盖了系统的passwd文件,很可怕的。当然了,ruby解释器需要设置适当权限,还有就是很多服务的访问者也需要设置适当的权限。

在进行文件名检查的时候,不用删除恶意的部分。较好的做法是设立白名单,然后检查文件名是否符合白名单设置,是否为可接受的字符。或者设计黑名单,将非法字符进行替换。遇到文件名包含非法字符的时候,要么拒绝,要么进行替换,不用删除。

用同步处理文件上传的过程,一个重大的漏洞就是拒绝服务攻击。攻击者可以同时上传多个文件,然后服务器就可能会由于超过负载而崩溃。

***是异步处理文件。先保存文件,然后在另外一个线程处理文件。

 

4.3.上传文件中的可执行代码

如果上传的文件中包含可执行代码,会很危险。再上传的时候,应该对上传的文件类型进行检查,不仅是后缀的检查,而且也要检查文件的真实类型。

如果webserver使用的是apache,不要将上传的文件放在DocumentRoot目录,这个目录中的文件,如果是代码文件,是可执行的。单独放在其他目录。

 

4.4.文件下载

不能随意下载任何目录的文件。

如果你的下载函数接受一个用户输入的参数,而且又不检查的话,用户就会下载一些重要的文件。


  1. send_file('/var/www/uploads/' + params[:filename]) 

 “../../../etc/passwd” 

一个好的做法就是将文件的目录存放在数据库中,还有id,然后根据这个目录下载文件。

 

 

未完待续。。。
 




本文转自 virusswb 51CTO博客,原文链接:http://blog.51cto.com/virusswb/1035871,如需转载请自行联系原作者