Sinatra and Rack Protection setting

感情迁移 提交于 2019-11-29 03:31:15

Using this in your Sinatra app should solve your problem:

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

A better solution may be to upgrade Sinatra to 1.4, which uses Rack::Protection 1.5 and should not cause the problem you are seeing.

The problem is that your version of RackProtection::JsonCsrf in is incompatible with CORS when you respond with Content-Type: application/json. Here is a snippet from the old json_csrf.rb in rack-protection:

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

You can see this rejects requests that have an application/json response when the referrer is not from the same host as the server.

This problem was solved in a later version of rack-protection, which now considers whether the request is an 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

If you are using Sinatra 1.3.2 and cannot upgrade the solution is to disable this particular protection. With CORS you are explicitly enabling cross-domain XHR requests. Sinatra lets you disable protection entirely, or disable specific components of Rack::Protection (see "Configuring Attack Protection" in the Sinatra docs).

Rack::Protection provides 12 middleware components that help defeat common attacks:

  • 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

At time of writing, all but four of these are loaded automatically when you use the Rack::Protection middleware (Rack::Protection::AuthenticityToken, Rack::Protection::FormToken, Rack::Protection::RemoteReferrer, and Rack::Protection::EscapedParams must be added explicitly).

Sinatra uses Rack::Protection's default settings with one exception: it only adds SessionHijacking and RemoteToken if you enable sessions.

And, finally, if you are trying to use CORS with Sinatra, you might try rack-cors, which takes care of a lot of the details for you.

If you see this issue, you are not using CORS (Cross-origin resource sharing), and are behind a reverse-proxy (such as nginx or apache), make sure that your reverse-proxy isn't stripping out host header and replacing it with localhost.

For example, in nginx you need to use proxy_set_header:

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

When the header is stripped out from a request, Rack::Protection believes it to be a CSRF attack.

Let me guess, you're testing with the Chrome app 'Dev HTTP Client' ? Try this instead:

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

From the rack protection module: "Supported browsers:: Google Chrome 2, Safari 4 and later"

This should work:

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

You probably wonder about chrome-extension://aejoelaoggembcahagimdiliamlcdmfm ? Well, that's what the rack protection gets as env['HTTP_ORIGIN'] when you send a POST request with the Chrome app.

al3xnull

Is this because you are not returning the allowed methods back in your options route?

A question here refers to it which notes the allowed methods back.

An extension here and middleware here might help you out.

rack-protection allows to specify a custom check starting from 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!