Rails的CSRF保护+ Angular.js:protect_from_forgery让我注销POST

如果protect_from_forgery中提到了protect_from_forgery选项,那么我可以login并执行任何GET请求,但是在第一次POST请求时,Rails会重置会话,这会将我注销。

我暂时closures了protect_from_forgery选项,但希望将其与Angular.js结合使用。 有没有办法做到这一点?

我认为从DOM读取CSRF值不是一个好的解决scheme,这只是一个解决方法。

这里是angularJS官方网站的文档格式http://docs.angularjs.org/api/ng.$http

由于只有在您的域上运行的JavaScript才能读取cookie,因此您的服务器可以确保XHR来自运行在您的域上的JavaScript。

要利用此(CSRF保护),您的服务器需要在第一个HTTP GET请求上的JavaScript可读会话Cookie(称为XSRF-TOKEN)中设置令牌。 在随后的非GET请求中,服务器可以validation该cookie是否与X-XSRF-TOKEN HTTP标头匹配

这是我的解决scheme基于这些指示:

首先,设置cookie:

 # app/controllers/application_controller.rb # Turn on request forgery protection protect_from_forgery after_filter :set_csrf_cookie_for_ng def set_csrf_cookie_for_ng cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery? end 

然后,我们应该validation每个非GET请求的令牌。
由于Rails已经用类似的方法构build,我们可以简单地覆盖它来附加我们的逻辑:

 # app/controllers/application_controller.rb protected # In Rails 4.2 and above def verified_request? super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN']) end # In Rails 4.1 and below def verified_request? super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] end 

如果你正在使用默认的Rails CSRF保护( <%= csrf_meta_tags %> ),你可以像这样configuration你的Angular模块:

 myAngularApp.config ["$httpProvider", ($httpProvider) -> $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') ] 

或者,如果你不使用CoffeeScript(什么!?):

 myAngularApp.config([ "$httpProvider", function($httpProvider) { $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content'); } ]); 

如果你愿意的话,你可以只在非GET请求上发送标题,如下所示:

 myAngularApp.config ["$httpProvider", ($httpProvider) -> csrfToken = $('meta[name=csrf-token]').attr('content') $httpProvider.defaults.headers.post['X-CSRF-Token'] = csrfToken $httpProvider.defaults.headers.put['X-CSRF-Token'] = csrfToken $httpProvider.defaults.headers.patch['X-CSRF-Token'] = csrfToken $httpProvider.defaults.headers.delete['X-CSRF-Token'] = csrfToken ] 

另外,一定要看看HungYuHei的答案 ,它涵盖了服务器上的所有基础,而不是客户端。

angular_rails_csrf gem自动添加对HungYuHei对所有控制器的回答中描述的模式的支持:

 # Gemfile gem 'angular_rails_csrf' 

合并所有以前的答案的答案,它依靠,你正在使用Deviseauthenticationgem。

首先,添加gem:

 gem 'angular_rails_csrf' 

接下来,将rescue_from块添加到application_controller.rb中:

 protect_from_forgery with: :exception rescue_from ActionController::InvalidAuthenticityToken do |exception| cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery? render text: 'Invalid authenticity token', status: :unprocessable_entity end 

最后,添加拦截器模块给你的angular度的应用程序。

 # coffee script app.factory 'csrfInterceptor', ['$q', '$injector', ($q, $injector) -> responseError: (rejection) -> if rejection.status == 422 && rejection.data == 'Invalid authenticity token' deferred = $q.defer() successCallback = (resp) -> deferred.resolve(resp) errorCallback = (resp) -> deferred.reject(resp) $http = $http || $injector.get('$http') $http(rejection.config).then(successCallback, errorCallback) return deferred.promise $q.reject(rejection) ] app.config ($httpProvider) -> $httpProvider.interceptors.unshift('csrfInterceptor') 

我看到了其他的答案,并认为他们是伟大的,深思熟虑。 我得到了我的Rails应用程序工作,虽然我认为是一个更简单的解决scheme,所以我想我会分享。 我的Rails应用程序带着这个默认的,

 class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception end 

我读了评论,似乎这是我想要使用angular度和避免csrf错误。 我改变了这个,

 class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :null_session end 

现在它工作! 我看不出有什么理由不应该这样做,但我很想听听其他海报的一些见解。

我在应用程序中使用了HungYuHei的答案。 我发现我正在处理一些额外的问题,其中一些是因为我使用Devise进行身份validation,另一些则是因为我的应用程序使用了默认设置:

 protect_from_forgery with: :exception 

我注意到相关的堆栈溢出问题和答案 ,我写了一个更详细的博客文章 ,总结了各种考虑。 该解决scheme的相关部分在应用程序控制器中:

  protect_from_forgery with: :exception after_filter :set_csrf_cookie_for_ng def set_csrf_cookie_for_ng cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery? end rescue_from ActionController::InvalidAuthenticityToken do |exception| cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery? render :error => 'Invalid authenticity token', {:status => :unprocessable_entity} end protected def verified_request? super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] end 

我发现了一个很快的黑客。 我所要做的只是以下几点:

一个。 在我看来,我初始化了一个$scopevariables,它包含了令牌,比方说在表单之前,或者在控制器初始化时更好:

 <div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'"> 

湾 在我的AngularJS控制器中,在保存我的新条目之前,我将这个标记添加到哈希中:

 $scope.addEntry = -> $scope.newEntry.authenticity_token = $scope.authenticity_token entry = Entry.save($scope.newEntry) $scope.entries.push(entry) $scope.newEntry = {} 

没有什么需要做的。

  angular .module('corsInterceptor', ['ngCookies']) .factory( 'corsInterceptor', function ($cookies) { return { request: function(config) { config.headers["X-XSRF-TOKEN"] = $cookies.get('XSRF-TOKEN'); return config; } }; } ); 

它在angularjs方面工作!

Interesting Posts