如何在Rails 5 API模式下启用CSRF

时间:2017-04-10 13:04:11

标签: api session cookies csrf ruby-on-rails-5

我有一个Rails API,它使用仅限http的cookie进行身份验证,因此我需要CSRF保护。据我所知,Rails社区似乎更喜欢在本地存储而不是cookie中存储jwt auth令牌。这避免了对CSRF的需求,但让您接触到XSS,这就是我们选择使用cookies + csrf的原因。

由于社区对本地存储的偏好,默认情况下禁用CSRF保护。我试图以有限的成功实现它。以下是我试图处理它的方法:

module V1
  class ApplicationController < ::ApplicationController
    include Concerns::Authentication
    include ActionController::RequestForgeryProtection
    protect_from_forgery

    protected

    def handle_unverified_request
      raise 'Invalid CSRF token'
    end

    after_action :set_csrf_cookie

    def set_csrf_cookie
      if current_user 
        cookies['X-CSRF-Token'] = form_authenticity_token
      end
    end
  end
end

在客户端,我可以看到令牌返回cookie。当我发出请求时,我也看到令牌出现在X-CSRF-Token标头中。到目前为止看起来都很顺利。

但是,verified_request?方法返回false,因此调用handle_unverified_request。单步执行Rails代码,我看到request.x_csrf_token中存在我的令牌,但是当针对session检查时,令牌似乎无法验证。我想知道的一件事是,如果我需要启用某些东西以使session正常工作,我理解会话管理未在API模式下默认启用。但是,如果是这种情况,我会希望尝试访问session对象爆炸,但他们没有,所以我不确定。

我是否犯了错误,或者是否有其他需要打开的中间件?或者我是否需要一种不同的方法来使用这种方案来实现CSRF?

1 个答案:

答案 0 :(得分:4)

我意识到这是一个过度思考问题的案例。我真的不需要Rails的伪造保护来为我做任何事情,或者检查session的价值,因为我的令牌的价值已经是一个cookie。以下是我如何解决它:

首先,基本控制器设置csrf cookie。对于注销或任何公共端点,如果有任何公共端点,将跳过此。

module V1
  class ApplicationController < ::ApplicationController
    include Concerns::Authentication
    include ActionController::RequestForgeryProtection

    after_action :set_csrf_cookie

    protected

    def set_csrf_cookie
      if current_user 
        cookies['X-CSRF-Token'] = form_authenticity_token
      end
    end
  end
end

然后我的经过身份验证的端点继承自检查身份验证令牌和csrf令牌的AuthenticatedController

module V1
  class AuthenticatedController < ApplicationController
    before_action :authenticate!

    def authenticate!
      raise AuthenticationRequired unless current_user && csrf_token_valid?
    end

    rescue_from AuthenticationRequired do |e|
      render json: { message: 'Authentication Required', code: :authentication_required }, status: 403
    end

    rescue_from AuthTokenExpired do |e|
      render json: { message: 'Session Expired', code: :session_expired }, status: 403
    end

    private

    def csrf_token_valid?
      Rails.env != 'production' || request.headers['X-CSRF-Token'] === cookies['X-CSRF-Token']
    end
  end
end

希望这有助于其他人尝试在Rails 5 API中使用CSRF + Cookie!