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'
合并所有以前的答案的答案,它依靠,你正在使用Devise
authenticationgem。
首先,添加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
我发现了一个很快的黑客。 我所要做的只是以下几点:
一个。 在我看来,我初始化了一个$scope
variables,它包含了令牌,比方说在表单之前,或者在控制器初始化时更好:
<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方面工作!