Rails 不安全的默认配置 —— 需要了解的13个安全陷阱 已翻译 100%

Lax 投递于 2013/03/28 10:35 (共 22 段, 翻译完成于 04-26)
阅读 7092
收藏 61
13
加载中

安全的默认值对于构建安全系统至关重要。如果开发者必须采取各种明确行动才能实现安全,最终即使最有经验的开发者也会忘记这么做。出于这个原因,安全专家说:

“不安全的默认值导致系统不安全。”

Rails作为相对安全的Web框架的名声是当之无愧的。对常见的攻击都有直接可用的防护:跨站脚本(XSS),跨站请求伪造(CSRF)和SQL注入。Rails核心开发成员的安全知识丰富,且关注安全。

然而,也有地方的默认行为可以做的更安全。这篇文章探讨了Rails 3存在的潜在安全性问题,一些已在Rails 4修复,一些仍然有风险。我希望这篇文章能帮助你保护自己的应用程序,同时也能激发Rails本身的改变。

Lax
Lax
翻译于 2013/03/28 14:03
2

Rails 3的问题

我们以主干中已修复的Rails 3问题来开始。修复这些问题时,Rails团队功不可没,但是仍然值得注意,因为很多程序将在Rails 2和3上运行多年。

1. 通过#match路由泄漏进行CSRF攻击

这里有一个例子,直接来自Rails 3生成的config/routes.rb文件:

WebStore::Application.routes.draw do
  # Sample of named route:
  match 'products/:id/purchase' => 'catalog#purchase',
    :as => :purchase
  # This route can be invoked with
  # purchase_url(:id => product.id)
end

这样做的结果,对于任何HTTP方法(GET,POST等),都将/products/:id/purchases路由到CatelogController#purchase方法。问题是,Rails的跨站请求伪造(CSRF)防护对GET请求无效。从执行CSRF防护的方法中看到:

def verified_request?
  !protect_against_forgery? ||
  request.get? ||
  form_authenticity_token ==
    params[request_forgery_protection_token] ||
  form_authenticity_token ==
    request.headers['X-CSRF-Token']
end

第二行跳过了CSRF检查:意味着如果request.get?为true,请求被认为是“verified”,CSRF检查被跳过。实际上,Rails源代码里,在该方法的前面就有注释:

Get应该是安全和无副作用的。

Lax
Lax
翻译于 2013/03/28 14:17
2

在程序里,你可以一直使用POST方法来访问/products/:id/purchase。但是因为路由器也允许GET请求,对于任何由#match路由的方法,攻击者都可以绕过CSRF保护。如果你的程序使用旧的通配符路由(已经不推荐),CSRF保护完全无效。

最佳实践: 对于不安全行为,不要使用GET。不要使用#match来添加路由(而应该用#post,#put等代替)。确保没有通配符路由。

解决方法: 现在,如果使用#match来添加路由,Rails需要你指定HTTP方法或via: :all。自动生成的config/routes.rb不再包含注释掉的#match路由。(通配符路由也被删除。)

Lax
Lax
翻译于 2013/03/28 14:26
1

2. 正则表达式中进行格式验证的锚点

观察下面的验证代码:

validates_format_of :name, with: /^[a-z ]+$/i

这是一个不明显的bug。开发者可能想强制要求整个name属性仅仅包含字母和空格。然而,它仅仅强制name属性至少一行由字母和空格组成。再看几个正则表达式匹配的例子来澄清一下:

>> /^[a-z ]+$/i =~ "Joe User"
=> 0 # Match

>> /^[a-z ]+$/i =~ " '); -- foo"
=> nil # No match

>> /^[a-z ]+$/i =~ "a\n '); -- foo"
=> 0 # Match
开发者其实应该用\A(字符串起始)和\z(字符串结束)锚点来代替^(行起始)和$(行结束)。正确的代码应该为:
validates_format_of :name, with: /\A[a-z ]+\z/i

你可能会说开发者错了,而你做对了。然而,正则表达式锚点的行为并不显而易见,尤其对于没考虑多行输入的开发者。(也许该属性仅仅漏出一个文本输入区input field,而不是文本框textararea)

Rails在保护开发者方面考虑的不错,这也是在Rails 4中所做到的。

最佳实践:在任何情况下,使用\A和\z作为正则表达式锚点,代替^和$。

修复方法: Rails 4为validates_format_of引入了支持多行的选项。如果你的正则表达式使用^和$而不是\A和\z,且不传递multiline: true,则Rails将抛出异常。这是创建安全默认行为的例子,且仍然在必要的场合提供控制选项来覆盖它。

Lax
Lax
翻译于 2013/04/01 14:23
1

3. 点击劫持

点击劫持或“UI纠正攻击”包括在一个可视框架内渲染目标站点,当受害者点击时就能欺骗他采取意想不到的行动。如果一个站点易受点击劫持攻击,一个攻击者可能欺骗用户采取非预期的行动,像一键购买,在Twitter上关注某人或改变他们的隐私设置。

为了抵御点击劫持攻击,一个站点必须防止自己被呈现在一个框架或它不能控制的iframe中。老的浏览器需要丑陋的“框架破坏”JavaScripts,而现代的浏览器支持可以指示浏览器是否应该允许该网站被加框的X-Frame-Options HTTP头。这个头很容易被包含,并且不可能破坏大部分网站,因此Rails应该默认包含了它。

最佳实践: 通过Twitter使用安全头RubyGem添加一个值为SAMEORIGIN或DENY的X-Fram-Options头。

修复方法: Rails4默认将X-Frame-Options头的值设为SAMEORIGIN。

X-Frame-Options: SAMEORIGIN
这告诉浏览器你的应用程序只能被源自相同域的页面加框。
jimmyjmh
jimmyjmh
翻译于 2013/04/06 13:36
1

4. 用户可读回话

默认的Rails 3会话存储使用署名的、未加密的cookies。虽然这可以包含会话不被篡改,对于攻击者来说,解码一个会话cookie的内容是轻而易举的事:

session_cookie = <<-STR.strip.gsub(/\n/, '')
BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTkwYThmZmQ3Zm
dAY7AEZJIgtzZWtyaXQGO…--4c50026d340abf222…
STR

Marshal.load(Base64.decode64(session_cookie.split("--")[0]))
# => {
#   "session_id"  => "90a8f...",
#   "_csrf_token" => "iUoXA...",
#   "secret"      => "sekrit"
# }

在一个会话中存储任何敏感信息都是不安全的。希望这是众所周知的,但即使一个用户的会话不包含敏感信息,也还是会带来奉献。通过解码会话数据,一个攻击者能够获取一些关于程序的内部结构的、有利于攻击的信息。例如,他可能知道系统用的是什么认证(Authlogic,Devise等待)。

虽然不会创建一个自身的弱点,但它可以帮助攻击者。任何有关应用程序如何工作的信息都可用来磨练功绩,有时也用来避免触发那些会给开发人员预警攻击正在进行的异常或绊网。

用户可读会话违反最小特权原则,因为即使会话数据必须传给访问者的浏览器,他也不需要能够读取这些数据。

最佳实践: 不要将任何你不想让攻击者访问的信息放到一个会话中。

修复方法: Rails 4将默认会话存储改为加密的。在没有解密密钥的情况下,用户在客户端无法解码会话的内容。

jimmyjmh
jimmyjmh
翻译于 2013/04/06 14:10
1

未解决的问题

这篇文章剩下的部分会讲一下在出版的时候Rails还存在的的安全风险。希望最少这里的一些会被修复,同时我将会在修复了的情况下更新这篇文章。

1. VerboseServerHeaders

默认的Rails服务器是WEBrick(部分是Ruby标准库),虽然它很少在产品模式下运行WEBrick。作为默认的选择,WEBrick返回在每个HTTP回复一个verboseServerHeader。

HTTP/1.1 200 OK
# …
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)
看下WEBrick的源代码,你可以看到头部是由一些关键性的信息组成:
"WEBrick/#{WEBrick::VERSION} " +
"(Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})",

这暴露的WEBrick的版本,同时也包括正在运行的特定的Ruby的补丁级别(因为发布日期与补丁级别匹配)。使用这些信息,spray和prey扫描者更有效地针对你的服务器,同时可以定制他们的攻击让其变得更有效了。

最好的实践: 避免在产品模式运行WEBrick。有其他更好的服务器,如Passenger,Unicorn,Thin和Puma。

修复: 虽然这个问题是起源于WEBrick的源代码,Rails应该配置WEBrick来使用更小的verboseServerHeader。简单点就“Ruby”似乎更好。

excepiton
excepiton
翻译于 2013/04/06 21:31
1

2. 绑定到0.0.0.0

如果你启动一个Rails服务器,你会看到一些像下面的东西:

$ ./script/rails server -e production
=> Booting WEBrick
=> Rails 3.2.12 application starting in production on http://0.0.0.0:3000

Rails在绑定到0.0.0.0(所以网卡)而不是127.0.0.1(仅本地网卡)。这会在开发模式和产品模式的上下文上都会产生安全风险。

在开发模式,Rails并不安全(举例来说,它渲染诊断500个页面)。此外,开发者可能加载一个混合了产品数据和测试数据的东西(例如用户名:admin/密码:admin)。在三藩市的咖啡店里扫描web服务器的3000端口将可能收到很好的目标。

excepiton
excepiton
翻译于 2013/04/06 21:36
1

在生产环境下,Rails必须通过代理服务器来运行。如果没有代理,IP欺骗攻击就会时常发生。如果Rails绑定了0.0.0.0,攻击者就可以轻松绕过代理服务器,直接攻击Rails服务器。

所以,绑定到127.0.0.1比默认的0.0.0.0会更安全。

最佳实践:保证生产环境仲的web服务器进程绑定了最小的一组接口。不要为了调试,而把生产环境数据导入到你的手提电脑。如果你必须这样做,尽可能少的导入数据,并且当它没用的时候立刻删除。

修复:Rails已经提供了一个绑定选项来修改服务器监听的IP地址。我们必须把默认的0.0.0.0修改为127.0.0.1。开发人员如果需要修改生产环境的绑定接口,可以通过修改部署配置来实现。

enixyu
enixyu
翻译于 2013/04/12 08:55
1

3. 记录Secret Tokens的版本

每一个Rails app都会获取一个很长,而且是随机生成的secret token,当使用rails new的时候,它会被生成并保存在config/initializers/secret_token.rb。里面的内容类似这样:

WebStore::Application.config.secret_token = '4f06a7a…72489780f'

因为rails自动创建secret token,所以很多开发者会忽略掉它。但是这个secret token就像是你的应用的管理员钥匙。如果你拥有了secret token,那样伪造会话和提升权限就会变得很容易。这是其中一个十分重要而且敏感的数据需要去保护的。加密是保护你的钥匙的最佳办法。

但是很不幸,rails并不能很好的处理这些secret token。secret_token.rb文件会被加入到版本控制当中,复制到GitHub,CI服务器和每一个开发人员的电脑。

enixyu
enixyu
翻译于 2013/04/12 09:06
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(10)

幻影浪子
幻影浪子
第6点很常见
Lax
Lax

引用来自“谭小鱼”的评论

引用来自“javacc”的评论

曾经自学了一下 ralis on ruby,挺不错的。

我最近也想学ROR, 请问你当初是怎么自学的, 有什么好的学习资料推荐吗

看这本书:Agile Web Development with Rails,现在已经是第四版了。
http://pragprog.com/book/rails4/agile-web-development-with-rails

书店可以买到中文版。
javacc
javacc

引用来自“谭小鱼”的评论

引用来自“javacc”的评论

曾经自学了一下 ralis on ruby,挺不错的。

我最近也想学ROR, 请问你当初是怎么自学的, 有什么好的学习资料推荐吗

就是 在 网上 找资料。先搭建环境,都有文档说明的,然后在做个hello world 慢慢就了解了
谭小鱼
谭小鱼

引用来自“javacc”的评论

曾经自学了一下 ralis on ruby,挺不错的。

我最近也想学ROR, 请问你当初是怎么自学的, 有什么好的学习资料推荐吗
rubyist
rubyist
靠,这水平也太差了。。倒是说说其中有几个是rails默认的??
javacc
javacc
曾经自学了一下 ralis on ruby,挺不错的。
紫电清霜
紫电清霜
沙滩~
tsl0922
tsl0922
好文!
oaoit
oaoit
摇椅~
乖猫的老公
乖猫的老公
沙发
返回顶部
顶部