Rails 5:如何纠正已完成的422无法处理的实体错误?

时间:2017-04-23 02:08:37

标签: ruby-on-rails authentication login csrf csrf-protection

我在Heroku中有一个Rails 5应用程序,它使用基于Michael Hartl的Ruby on Rails教程中的逻辑的会话控制器进行登录/注销。从Rails 3开始,我已经做了几年类似的逻辑。我的应用程序在localhost,生产状态下使用Passenger在我的服务器和Heroku上工作。正在执行相同的代码。几天前,当我尝试在服务器上登录应用程序的生产版本时,我开始遇到以下错误。但是,我仍然可以在localhost / development和Heroku上登录我的应用程序。

I, [2017-04-22T20:15:50.242323 #65501]  INFO -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c] Started POST "/sessions" for 127.0.0.1 at 2017-04-22 20:15:50 -0500
I, [2017-04-22T20:15:50.243524 #65501]  INFO -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c] Processing by SessionsController#create as HTML
I, [2017-04-22T20:15:50.243945 #65501]  INFO -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c]   Parameters: {"utf8"=>"✓", "authenticity_token"=>"RsEqEfiDw82E2YI17SVkkUcxhiqtUw75nC1i9GJmIYZlwjg6o0mXiHehCHP627iTOjyQoPA+mrmi+Bh99BxICQ==", "session"=>{"email_user"=>"pamela", "password"=>"[FILTERED]"}, "commit"=>"Login"}
W, [2017-04-22T20:15:50.245161 #65501]  WARN -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c] Can't verify CSRF token authenticity.
I, [2017-04-22T20:15:50.246050 #65501]  INFO -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c] Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)
F, [2017-04-22T20:15:50.248110 #65501] FATAL -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c]   
F, [2017-04-22T20:15:50.248546 #65501] FATAL -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c] ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
F, [2017-04-22T20:15:50.248894 #65501] FATAL -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c]   
F, [2017-04-22T20:15:50.249263 #65501] FATAL -- : [40a379f9-27c2-4923-a9ce-f310f96dbc4c] actionpack (5.0.2) lib/action_controller/metal/request_forgery_protection.rb:195:in `handle_unverified_request'
[40a379f9-27c2-4923-a9ce-f310f96dbc4c] actionpack (5.0.2) lib/action_controller/metal/request_forgery_protection.rb:223:in `handle_unverified_request'
[40a379f9-27c2-4923-a9ce-f310f96dbc4c] actionpack (5.0.2) lib/action_controller/metal/request_forgery_protection.rb:218:in `verify_authenticity_token'

application_controller

protect_from_forgery with: :exception
include SessionsHelper

在我能够成功登录的同一台计算机上从localhost登录:

Started POST "/sessions" for ::1 at 2017-04-22 22:26:23 -0500
Processing by SessionsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"zhkoMofyRdPcwQ7v2GOYltlRv2PJM8duzbuxJsa5dAy0je/yj1CRedQM/H1Zku2ukQbbG7YF6OZd4ootV0qolA==", "session"=>{"email_user"=>"myuserid", "password"=>"[FILTERED]"}, "remember_me"=>"1", "commit"=>"Login"}
  [1m[36mUser Load (74.2ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2[0m  [["email", "myuserid"], ["LIMIT", 1]]
  [1m[36mUser Load (0.8ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."username" = $1 LIMIT $2[0m  [["username", "myuserid"], ["LIMIT", 1]]
  [1m[35m (0.2ms)[0m  [1m[35mBEGIN[0m
  [1m[35mSQL (22.6ms)[0m  [1m[33mUPDATE "users" SET "remember_token" = $1, "updated_at" = $2 WHERE "users"."id" = $3[0m  [["remember_token", "75f5152d815e24e6ce7709cc93f34265ad9161be"], ["updated_at", 2017-04-23 03:26:23 UTC], ["id", 3]]
  [1m[35m (26.4ms)[0m  [1m[35mCOMMIT[0m
Redirected to http://localhost:3000/
Completed 302 Found in 229ms (ActiveRecord: 124.5ms)


Started GET "/" for ::1 at 2017-04-22 22:26:23 -0500
Processing by PagesController#home as HTML
  [1m[36mUser Load (7.4ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."remember_token" = $1 LIMIT $2[0m  [["remember_token", "75f5152d815e24e6ce7709cc93f34265ad9161be"], ["LIMIT", 1]]
  Rendering pages/home.html.erb within layouts/application
  Rendered pages/home.html.erb within layouts/application (1.1ms)
  Rendered layouts/_meta_data.erb (2.3ms)
  Rendered layouts/_shim.html.erb (0.4ms)
  Rendered layouts/_header.html.erb (1.1ms)
  Rendered layouts/_systemmessage.html.erb (0.5ms)
  Rendered layouts/_footer.html.erb (1.0ms)
Completed 200 OK in 270ms (Views: 236.8ms | ActiveRecord: 7.4ms)

我有搜索解决方案,并且几乎所有人都说关闭这个我不想做的事情,特别是考虑到这是一个数据库维护应用程序。我在代码的三次执行之间可以看到的唯一区别是我的表中的remember_token的值,因为每个版本使用不同的数据库。我尝试将数据库上的remember_token字段设置为null但我仍然得到错误。我没有改变任何与登录/注销过程相关的内容,所以我真的很困惑。我将克隆Heroku中的内容并重试。

这是我在Rails软件开发近六年来第一次看到这个错误。我意识到我很可能有攻击,但我不知道如何调试此错误。相同的代码适用于Heroku和localhost。

更新:我查看了服务器上生产应用程序的页面源代码。这是我尝试登录并收到错误之前的标题。使用我的localhost版本和Heroku版本,登录后会显示以下两个语句。

<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="JoPFlDzY0SYSpOSq0dXgafSICgZ9qXJz/x4tX31owZPh3cu7fNR553iGPn5c+RnZVOuxiEolnoOin0Rkoay4Ag==" />

更新:尝试覆盖rails/actionpack/lib/action_controller/metal/request_forgery_protection.rb中第195行引发的类InvalidAuthenticityToken。目前这个班级没有任何陈述。由于我没有看到任何相关说明或者甚至可能有这样的说明,所以在这一点上进行了大量猜测。

require 'action_controller/metal/request_forgery_protection'

class XxxLogger < ActionPack::ActionController::RequestForgeryProtection
  source_root File.join(File.dirname(ActionPack::ActionController::RequestForgeryProtection.instance_method(:handle_unverified_request).source_location.first), "templates")

  class InvalidAuthenticityToken < ActionControllerError #:nodoc:
    logger.warn "Can't verify CSRF token authenticity
  end

end

1 个答案:

答案 0 :(得分:0)

有2个令牌需要验证,form tokencsrf token。任何一个令牌失败,都会抛出InvalidAuthenticityToken例外。

在生产部署目录中,运行bundle show rails,找到确切的rails目录,然后将日志添加到相关的rails代码中,找到详细原因。

以这种方式添加日志,logger.warn "Can't verify CSRF token authenticity.",这就是rails输出日志的方式。

actionpack/lib/action_controller/metal/request_forgery_protection.rb文件中,找到any_authenticity_token_valid?首先失败的原因。

并检查此问题https://github.com/rails/rails/issues/24257#issuecomment-212203983,更改为users_path,而不是users_url

并照顾好这一点, For Rails 5, note that protect_from_forgery is no longer prepended to the before_action chain, so if you have set authenticate_user before protect_from_forgery, your request will result in "Can't verify CSRF token authenticity." To resolve this, either change the order in which you call them, or use protect_from_forgery prepend: true.