Docker中的端口转发的Rails应用似乎会导致CSRFexception

我有一个在Docker容器中运行的Rails应用程序,它被分配了一个IP 172.17.0.3 。 对主机51.xxx传入请求被转发到172.17.0.3的rails应用程序。 更具体地说,这是这样做的:

 docker run -p 8080:8080 rails_app 

但是,Rails应用程序抛出当用户尝试访问某些页面时Can't verify CSRF token authenticity错误。 我怀疑Rails认为传入的请求是攻击,因为目标的ip不匹配Rails应用程序的ip – 即用户请求被引导到主机51.xxx ,而Rails的实际位置是172.17.0.3

有什么办法让我告诉Rails这些请求是合法的吗? 作为附加信息,我使用devise进行身份validation,并使用unicorn作为服务器。

你们中的一些人可能会试图build议将protect_from_forgery with: :exception更改protect_from_forgery with: :exception :null_session ,但是如果没有放在代理之后,应用程序就可以正常工作。 另外,当我改变那个部分的时候,一些逻辑将不起作用,因为我认为这个设置是以处理用户会话的方式出现的。

这是我的networking的布局:

 (user from public network) ----> (proxy) ----> (rails app on a private network) (202.xxx) (51.xxx) (172.xxx) 

编辑:该应用程序在development设置。 这是我在log/development.log文件中得到的错误。

 Started POST "/register" for 202.xxx at 2014-11-18 02:27:11 +0000 Processing by UsersController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"aBG3nIAKK1ALMJ1DDYFlMkmqISMBMZc3iLmaeD2byG8=", "user"=>{"email"=>"email@gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}} Can't verify CSRF token authenticity Completed 422 Unprocessable Entity in 2ms ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken): actionpack (4.1.4) lib/action_controller/metal/request_forgery_protection.rb:176:in `handle_unverified_request' actionpack (4.1.4) lib/action_controller/metal/request_forgery_protection.rb:202:in `handle_unverified_request' devise (3.4.0) lib/devise/controllers/helpers.rb:251:in `handle_unverified_request' actionpack (4.1.4) lib/action_controller/metal/request_forgery_protection.rb:197:in `verify_authenticity_token' activesupport (4.1.4) lib/active_support/callbacks.rb:424:in `block in make_lambda' activesupport (4.1.4) lib/active_support/callbacks.rb:160:in `call' activesupport (4.1.4) lib/active_support/callbacks.rb:160:in `block in halting' activesupport (4.1.4) lib/active_support/callbacks.rb:166:in `call' activesupport (4.1.4) lib/active_support/callbacks.rb:166:in `block in halting' activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `call' activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional' activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `call' activesupport (4.1.4) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional' activesupport (4.1.4) lib/active_support/callbacks.rb:86:in `run_callbacks' actionpack (4.1.4) lib/abstract_controller/callbacks.rb:19:in `process_action' actionpack (4.1.4) lib/action_controller/metal/rescue.rb:29:in `process_action' actionpack (4.1.4) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action' activesupport (4.1.4) lib/active_support/notifications.rb:159:in `block in instrument' activesupport (4.1.4) lib/active_support/notifications/instrumenter.rb:20:in `instrument' activesupport (4.1.4) lib/active_support/notifications.rb:159:in `instrument' actionpack (4.1.4) lib/action_controller/metal/instrumentation.rb:30:in `process_action' actionpack (4.1.4) lib/action_controller/metal/params_wrapper.rb:250:in `process_action' activerecord (4.1.4) lib/active_record/railties/controller_runtime.rb:18:in `process_action' actionpack (4.1.4) lib/abstract_controller/base.rb:136:in `process' actionview (4.1.4) lib/action_view/rendering.rb:30:in `process' actionpack (4.1.4) lib/action_controller/metal.rb:196:in `dispatch' actionpack (4.1.4) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch' actionpack (4.1.4) lib/action_controller/metal.rb:232:in `block in action' actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:82:in `call' actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:82:in `dispatch' actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:50:in `call' actionpack (4.1.4) lib/action_dispatch/routing/mapper.rb:45:in `call' actionpack (4.1.4) lib/action_dispatch/journey/router.rb:71:in `block in call' actionpack (4.1.4) lib/action_dispatch/journey/router.rb:59:in `each' actionpack (4.1.4) lib/action_dispatch/journey/router.rb:59:in `call' actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:678:in `call' omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' omniauth (1.2.2) lib/omniauth/strategy.rb:186:in `call!' omniauth (1.2.2) lib/omniauth/strategy.rb:164:in `call' warden (1.2.3) lib/warden/manager.rb:35:in `block in call' warden (1.2.3) lib/warden/manager.rb:34:in `catch' warden (1.2.3) lib/warden/manager.rb:34:in `call' rack (1.5.2) lib/rack/etag.rb:23:in `call' rack (1.5.2) lib/rack/conditionalget.rb:35:in `call' rack (1.5.2) lib/rack/head.rb:11:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/params_parser.rb:27:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/flash.rb:254:in `call' rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context' rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/cookies.rb:560:in `call' activerecord (4.1.4) lib/active_record/query_cache.rb:36:in `call' activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:621:in `call' activerecord (4.1.4) lib/active_record/migration.rb:380:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call' activesupport (4.1.4) lib/active_support/callbacks.rb:82:in `run_callbacks' actionpack (4.1.4) lib/action_dispatch/middleware/callbacks.rb:27:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/reloader.rb:73:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/remote_ip.rb:76:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call' railties (4.1.4) lib/rails/rack/logger.rb:38:in `call_app' railties (4.1.4) lib/rails/rack/logger.rb:20:in `block in call' activesupport (4.1.4) lib/active_support/tagged_logging.rb:68:in `block in tagged' activesupport (4.1.4) lib/active_support/tagged_logging.rb:26:in `tagged' activesupport (4.1.4) lib/active_support/tagged_logging.rb:68:in `tagged' railties (4.1.4) lib/rails/rack/logger.rb:20:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/request_id.rb:21:in `call' rack (1.5.2) lib/rack/methodoverride.rb:21:in `call' rack (1.5.2) lib/rack/runtime.rb:17:in `call' activesupport (4.1.4) lib/active_support/cache/strategy/local_cache_middleware.rb:26:in `call' rack (1.5.2) lib/rack/lock.rb:17:in `call' actionpack (4.1.4) lib/action_dispatch/middleware/static.rb:64:in `call' rack-cors (0.2.9) lib/rack/cors.rb:54:in `call' rack (1.5.2) lib/rack/sendfile.rb:112:in `call' railties (4.1.4) lib/rails/engine.rb:514:in `call' railties (4.1.4) lib/rails/application.rb:144:in `call' rack (1.5.2) lib/rack/lint.rb:49:in `_call' rack (1.5.2) lib/rack/lint.rb:37:in `call' rack (1.5.2) lib/rack/showexceptions.rb:24:in `call' rack (1.5.2) lib/rack/commonlogger.rb:33:in `call' sinatra (1.4.5) lib/sinatra/base.rb:217:in `call' rack (1.5.2) lib/rack/chunked.rb:43:in `call' rack (1.5.2) lib/rack/content_length.rb:14:in `call' unicorn (4.8.3) lib/unicorn/http_server.rb:576:in `process_client' unicorn (4.8.3) lib/unicorn/http_server.rb:670:in `worker_loop' unicorn (4.8.3) lib/unicorn/http_server.rb:525:in `spawn_missing_workers' unicorn (4.8.3) lib/unicorn/http_server.rb:140:in `start' unicorn (4.8.3) bin/unicorn:126:in `<top (required)>' 

从粗略阅读' protect_from_forgery方法 ',我们发现如下:

  def protect_from_forgery(options = {}) self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session) self.request_forgery_protection_token ||= :authenticity_token prepend_before_action :verify_authenticity_token, options append_after_action :verify_same_origin_request end 

其中有一个名为“ verify_authenticity_token ”的动作callback。 如果我们看看它的来源,我们发现以下几点:

  def verify_authenticity_token mark_for_same_origin_verification! if !verified_request? logger.warn "Can't verify CSRF token authenticity" if logger handle_unverified_request end end 

从那里我们注意到它叫' verified_request? ”。

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

鉴于所提出的例外的性质,我认为这些条件中的一个或多个条件没有得到满足。 我认为这与IP寻址没有任何关系。

如果您的Rails应用程序通过非SSL向您的代理发言,那么您的ActiveRecord :: SessionStore可能会因为这种情况而发生问题。

我们的修复是让会话存储不安全:

OurApplication::Application.config.session_store :active_record_store, secure: false

编辑:仍然没有修复呢…我们可能将不得不使SSL终止在应用程序,而不是代理。

所以对于我们来说,这个问题最终与SSL没有任何关系。 我们在第一次加载的页面上执行了一个JavaScript调用,这个调用是通过POST来执行后端服务的握手,但是我们没有正确configurationHAProxy来调用这个服务,所以POST是打铁路。 尽pipeRails为路由返回了404,但是由于请求中丢失了CSRF令牌,它也重置了会话。 修复HAProxy的路由问题解决了这个问题。

我们的场景可能与您的情况几乎没有任何关系,而在Rails 4中,他们使protect_from_forgery的默认行为是引发exception,而不是重置会话。 哦,而且我们最终也需要将会话存储设置为不安全:

OurApplication::Application.config.session_store :active_record_store, secure: false