Рельсы CSRF защита + угловая.js: защита от подделки заставляет меня выходить на пост


Если protect_from_forgery опция упоминается в application_controller, тогда я могу войти в систему и выполнить любые запросы GET, но на самых первых запросах POST Rails сбрасывает сеанс, который выводит меня из системы.

Я повернул protect_from_forgery опция временно отключена, но хотелось бы использовать ее с угловой.js. Есть ли какой-то способ сделать это?

8 125

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 = {}

больше ничего не нужно делать.

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

он работает на стороне angularjs!