Sinatra 和机架保护设置

Posted

技术标签:

【中文标题】Sinatra 和机架保护设置【英文标题】:Sinatra and Rack Protection setting 【发布时间】:2012-05-17 14:17:35 【问题描述】:

我正在使用 Sinatra 和 CORS 来接受域 A (hefty.burger.com) 上的文件上传。域 B (fizzbuzz.com) 有一个表单,可以将文件上传到 A 上的路由。

我有一个 options 路由和一个 post 路由,都命名为 '/uploader'。

options '/uploader' do
  headers 'Access-Control-Allow-Origin' => 'http://fizz.buzz.com',
  'Access-Control-Allow-Methods' => 'POST'
  200
end 

post '/uploader' do
  ... 
  content_type :json
  [:mary => 'little lamb'].to_json
end

选项首先被点击...并且它有效..然后帖子被点击并返回 403。

如果我禁用保护,帖子可以工作...我需要从列表中排除哪种保护以保持保护但允许这些帖子通过?

我最近才被 Heroku 上新的 Rack 保护所烧伤,这让我有些悲伤……有人能指点一下这里该怎么做吗?我之所以这么说,是因为突然间我看到了带有会话劫持问题警报的日志条目(几乎可以肯定是因为应用程序运行 > 1 Dyno)。我在我的 Gemfile.lock 中看到 rack-protection (1.2.0),即使我从来没有要求它......我的清单中的某些东西正在调用它,所以它被加载了,但我的 Sinatra 应用程序中没有任何东西甚至试图要求它或进行设置。

【问题讨论】:

如果您启用了日志记录,它应该显示负责的保护,例如attack prevented by RemoteToken(前面有记录器序言)。 我在 Rack Protection 1.5.3 上看到了这个。机架保护 eb7e4c9a176d 没有发生这种情况。 【参考方案1】:

在您的 Sinatra 应用程序中使用它应该可以解决您的问题:

set :protection, :except => [:json_csrf]

更好的解决方案可能是将 Sinatra 升级到 1.4,它使用 Rack::Protection 1.5 并且不会导致您看到 the problem。

问题是当您使用 Content-Type: application/json 响应时,您的 RackProtection::JsonCsrf 版本与 CORS 不兼容。这是机架保护中old json_csrf.rb 的一个 sn-p:

def call(env)
  status, headers, body = app.call(env)
  if headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
    if referrer(env) != Request.new(env).host
      result = react(env)
      warn env, "attack prevented by #self.class"
    end
  end
  result or [status, headers, body]
end

当引用者与服务器不是来自同一主机时,您可以看到这会拒绝具有application/json 响应的请求。

这个问题在更高版本的 rack-protection 中得到解决,现在考虑请求是否是 XMLHttpRequest:

   def has_vector?(request, headers)
    return false if request.xhr?
    return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
    origin(request.env).nil? and referrer(request.env) != request.host
  end

如果您使用的是 Sinatra 1.3.2 并且无法升级,解决方案是禁用此特定保护。使用 CORS,您可以显式启用跨域 XHR 请求。 Sinatra 允许您完全禁用保护,或禁用 Rack::Protection 的特定组件(请参阅 Sinatra 文档中的 "Configuring Attack Protection")。

Rack::Protection 提供12 middleware components 帮助抵御常见攻击:

Rack::Protection::AuthenticityToken Rack::Protection::EscapedParams Rack::Protection::FormToken Rack::Protection::FrameOptions Rack::Protection::HttpOrigin Rack::Protection::IPSpoofing Rack::Protection::JsonCsrf Rack::Protection::PathTraversal Rack::Protection::RemoteReferrer Rack::Protection::RemoteToken Rack::Protection::SessionHijacking Rack::Protection::XssHeader

在撰写本文时,当您使用 Rack::Protection 中间件(Rack::Protection::AuthenticityTokenRack::Protection::FormTokenRack::Protection::RemoteReferrerRack::Protection::EscapedParams 必须明确添加)时,除了四个之外,所有这些都会自动加载。

Sinatra 使用 Rack::Protection 的默认设置和 one exception:如果您启用会话,它只会添加 SessionHijackingRemoteToken

最后,如果您尝试将 CORS 与 Sinatra 一起使用,您可以尝试 rack-cors,它会为您处理很多细节。

【讨论】:

但是你把那行放在哪个文件里?令人困惑。【参考方案2】:

如果您看到此问题,说明您没有使用 CORS(跨域资源共享),并且位于反向代理(例如 nginx 或 apache)之后,请确保您的反向代理没有剥离 @ 987654321@ 标头并将其替换为 localhost。

例如在nginx中你需要使用proxy_set_header:

location / 
    proxy_pass http://localhost:9296;
    proxy_set_header Host $host;

当从请求中剥离标头时,Rack::Protection 认为这是一次 CSRF 攻击。

【讨论】:

你的帖子就像是我的架构的手套,但是在我的 nginx 配置中添加 proxy_set_header Host $host; 并没有解决它。【参考方案3】:

让我猜猜,您正在使用 Chrome 应用程序“Dev HTTP Client”进行测试?试试这个:

curl -v -X POST http://fizz.buzz.com/uploader

从机架保护模块: “支持的浏览器:: Google Chrome 2、Safari 4 及更高版本”

这应该可行:

class App < Sinatra::Base
  ...
  enable :protection
  use Rack::Protection, except: :http_origin
  use Rack::Protection::HttpOrigin, origin_whitelist: ["chrome-extension://aejoelaoggembcahagimdiliamlcdmfm", "http://fizz.buzz.com"]

  post '/uploader' do
    headers \
      'Allow'   => 'POST',
      'Access-Control-Allow-Origin' => 'http://fizz.buzz.com'
    body "it work's !"
  end

您可能想知道 chrome-extension://aejoelaoggembcahagimdiliamlcdmfm 吗?好吧,当您使用 Chrome 应用程序发送 POST 请求时,机架保护会得到 env['HTTP_ORIGIN']

【讨论】:

在新的 Sinatra 应用程序中,我必须将 use Rack::Protection::HttpOrigin, origin_whitelist: ["chrome-extension://aejoelaoggembcahagimdiliamlcdmfm", "http://fizz.buzz.com"] 更改为 set :protection, origin_whitelist: ['chrome-extension://aejoelaoggembcahagimdiliamlcdmfm']【参考方案4】:

这是因为您没有在选项路由中返回允许的方法吗?

一个问题here 引用它,它记录了允许的方法。

扩展 here 和中间件 here 可能会帮助您。

【讨论】:

不,我正在设置正确的选项。我明确允许 POST,但 POST 返回 403 被禁止。我不确定机架保护的哪一部分在控制它。这些文档根本不清楚,似乎没有包含任何有关 CORS 的内容。 大卫,我昨晚一定在太空中。你可以尝试设置use Rack::Protection, :except =&gt; :json_csrf吗?有人对 rack::protection 的 git repo about Rack::Protection and CORS 也有问题。【参考方案5】:

rack-protection 允许从 2.0.0 开始指定自定义检查:

set :protection, :allow_if => lambda |env| env['HTTP_REFERER'] && URI(env['HTTP_REFERER']).host == 'fizz.buzz.com'

https://github.com/sinatra/sinatra/blob/a2fe3e698b19ac4065f166f1727afd31d0e72f95/rack-protection/lib/rack/protection/json_csrf.rb#L39

【讨论】:

以上是关于Sinatra 和机架保护设置的主要内容,如果未能解决你的问题,请参考以下文章

Sinatra Route什么都不返回

机架会话在 Chrome 中丢失

设置布局的 Sinatra 方法

为 Sinatra 设置默认 content_type

markdown 使用Active Record设置Sinatra

跨多个域名使用 Sinatra 会话变量?