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

Sam*_*amo 3 api cookies session csrf ruby-on-rails-5

我有一个通过 http-only cookie 进行身份验证的 Rails API,因此我需要 CSRF 保护。据我所知,Rails 社区似乎更喜欢将 jwt auth 令牌存储在本地存储而不是 cookie 中。这避免了对 CSRF 的需要,但会将您暴露给 XSS,这就是我们选择使用 cookie + 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
Run Code Online (Sandbox Code Playgroud)

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

但是,该verified_request?方法返回 false,因此handle_unverified_request被调用。单步执行 Rails 代码,我看到我的令牌存在于 中request.x_csrf_token,但在对照session. 我在这里想知道的一件事是我是否需要启用某些东西才能session正常工作,因为我知道会话管理在 API 模式下没有打开是默认的。但是,如果是这样的话,我有点期望访问session对象的尝试会爆炸,而他们没有,所以我不确定。

我犯了一个错误,还是我需要打开其他一些中间件?或者我是否需要完全不同的方法来使用此方案启用 CSRF?

Sam*_*amo 6

我意识到这是一个过度思考问题的案例。我真的不需要 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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

  • 不,这是一个普通的饼干。您需要验证它,因为客户端应该从 cookie 中获取值并将其放在请求标头中。这就是 CSRF 的全部意义。如果用户访问向您的站点发出请求的其他站点(称为站点 X),则令牌将始终位于 cookie 中。站点 X 不知道将该令牌放入标头中,但“您的”客户端知道。因此,您的客户端通过将令牌放入标头中来获取访问权限,而站点 X 会被拒绝。合理? (2认同)