使用 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
请求将不会被检查。
在一个 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
请求将不会被检查。