Рельсы CSRF защита + угловая.js: защита от подделки заставляет меня выходить на пост
Если protect_from_forgery
опция упоминается в application_controller, тогда я могу войти в систему и выполнить любые запросы GET, но на самых первых запросах POST Rails сбрасывает сеанс, который выводит меня из системы.
Я повернул protect_from_forgery
опция временно отключена, но хотелось бы использовать ее с угловой.js. Есть ли какой-то способ сделать это?
8 ответов:
Я думаю, что чтение CSRF-значения из DOM не является хорошим решением, это просто обходной путь.
вот форма документа angularJS официальный сайт http://docs.angularjs.org/api/ng.$http:
Так как только JavaScript, который работает на вашем домене, может прочитать cookie, ваш сервер может быть уверен, что XHR пришел из JavaScript, работающего на вашем домене.
чтобы воспользоваться этим (защита CSRF), ваш сервер должен установить токен в сеансе чтения JavaScript cookie называется XSRF-TOKEN при первом запросе HTTP GET. На последующих не-GET запросы сервер может проверить, что cookie соответствует X-XSRF-токен HTTP заголовок
вот мое решение, основанное на этих инструкциях:
во-первых, установите cookie:
# app/controllers/application_controller.rb # Turn on request forgery protection protect_from_forgery after_action :set_csrf_cookie def set_csrf_cookie cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery? end
затем, мы должны проверить маркер на каждый запрос GET.
Поскольку Rails уже построен с помощью аналогичного метода, мы можем просто переопределить его добавьте нашу логику:# 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
если вы используете защиту CSRF Rails по умолчанию (
<%= csrf_meta_tags %>
), вы можете настроить свой угловой модуль такой: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 ]
кроме того, не забудьте проверить ответ Хунгюхэя, который охватывает все базы на сервере, а не на клиенте.
на angular_rails_csrf gem автоматически добавляет поддержку шаблона, описанного в ответ Хунгюхэя для всех контроллеров:
# Gemfile gem 'angular_rails_csrf'
ответ, который объединяет все предыдущие ответы, и он полагается, что вы используете
Devise
камень аутентификации.прежде всего, добавьте драгоценный камень:
gem 'angular_rails_csrf'
далее добавить
rescue_from
блок в application_controller.РБ: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
и, наконец, добавить модуль перехватчика к вам угловой приложение.
# 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, хотя с тем, что я думал, было более простым решением, поэтому я подумал, что поделюсь. Мое приложение 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 для аутентификации, а некоторые из-за значения по умолчанию, которое я получил с моим приложением:
protect_from_forgery with: :exception
замечу, соответствующие вопрос переполнения стека и ответы там, а я написал гораздо более многословно блоге это обобщает различные соображения. Части этого решение, которые актуальны здесь, в контроллере приложения:
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
Я нашел очень быстрый хак для этого. Все, что мне нужно было сделать, это следующее:
a. на мой взгляд, я инициализирую a
$scope
переменная, которая содержит токен, скажем, перед формой, или даже лучше при инициализации контроллера:<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">
b. в моем контроллере AngularJS, перед сохранением моей новой записи, я добавляю маркер в хэш:
$scope.addEntry = -> $scope.newEntry.authenticity_token = $scope.authenticity_token entry = Entry.save($scope.newEntry) $scope.entries.push(entry) $scope.newEntry = {}
больше ничего не нужно делать.