使用 CSRF 令牌或其他身份验证保护 API

Protect API with CSRF token or other authentication

在一个 Rails 项目中,我正在设置自己的 API,我想通过一些 jQuery 调用从同一个站点调用它。

比如我有一条路线POST /api/v1/employee.

我希望此 API 调用可供我网站的用户使用,但也可用于调用此 API 并使用(例如)基本身份验证的外部客户端。

所以我想保护我的 API 免受 CSRF 的影响,当调用来自我自己的站点时,当它来自外部客户端时,我希望它检查 [=] 中的用户名和密码请求的 74=]。

我设置 jQuery 将我页面的元标记中指定的 csrf 令牌添加到它对 API 所做的每个请求。

$.ajaxSetup({
      beforeSend: function(xhr) {
          xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
      }
});

但现在我不确定从这里去哪里。

在我的控制器中,我添加了以下行,使我的客户端能够毫无问题地连接到 API,但是我没有任何保护。

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }

如果我为此控制器设置 before_filter,它 总是 检查用户名和密码,即使 csrf 令牌可用。

before_filter :authentication_check

^ authentication_check 是我创建的一种方法,它执行简单的基本身份验证检查(只是为了实现这一点)。

我还尝试了以下操作:

protect_from_forgery with: :exception, :unless => :valid_user?

但这可能并不像我想的那样。 valid_user? 是一种检查基本身份验证和 returns 用户是否正确的方法。

我想以某种方式将这两件事结合起来。如果没有 session(所以它和外部客户端连接)我想在 username/password 上进行身份验证,否则就使用正常的 CSRF 保护。

2015 年 11 月 25 日更新

好的,我现在有以下针对 POST 请求的解决方案。仍然需要添加 GET 等,但这里是

在我的控制器中我有这个:

protect_from_forgery with: :exception, unless: :valid_apikey

我的:valid_apikey thing方法是这样定义的:

def valid_apikey

    result = authenticate_or_request_with_http_token do |token, options|
        !User.find_by_token(token).nil?
    end
    result == true

end

因此,如果我是正确的,当请求提供我的数据库中的令牌时,将跳过 CSRF 检查。当令牌不在我的数据库中时,authenticate_or_request_with_http_token 方法 returns 和 Array(不同于 true)。

我建议您不必为您的 protect_from_forgery

设置例外

你可以简单地这样做

class ApplicationController < ActionController::Base
  protect_from_forgery

  def handle_unverified_request
    # your logic to handle request coming from outside your domain
    # if user is not valid then
    raise(ActionController::InvalidAuthenticityToken)
    # else allow to bypass
  end 

这样你就可以控制来自你的域的正常请求,你不必每次都通过 if 条件并点击数据库。

好的,我想这个问题已经解决了。我还希望我的 GET 请求检查 CSRF 令牌。

在我的控制器中,我执行以下操作:

def verified_request?
    if !request.headers["X-API-KEY"].nil?
        !ApiClient.find_by_token(request.headers["X-API-KEY"]).nil?
    else
        !protect_against_forgery? ||
            valid_authenticity_token?(session, params[request_forgery_protection_token]) ||
            valid_authenticity_token?(session, request.headers['X-CSRF-Token'])
    end
end

这会覆盖继承的 ApplicationController 的 verified_request? 方法。我不能只调用 super,因为这样我的 GET 请求将不会被检查。