Rails CSRF Protection + Angular.js : protect_from_forgery는 POST에서 로그 아웃하도록합니다
protect_from_forgery
옵션이 application_controller에 언급되어 있으면 로그인하고 GET 요청을 수행 할 수 있지만 처음 POST 요청에서 Rails는 세션을 재설정하여 로그 아웃합니다.
나는되어 protect_from_forgery
일시적으로 해제 옵션을하지만, Angular.js와 함께 사용하고 싶습니다. 그렇게 할 방법이 있습니까?
DOM에서 CSRF 값을 읽는 것이 좋은 해결책은 아니라고 생각합니다.
다음은 angularJS 공식 웹 사이트 http://docs.angularjs.org/api/ng.$http 의 문서 양식입니다 .
도메인에서 실행되는 JavaScript만이 쿠키를 읽을 수 있으므로 서버는 XHR이 도메인에서 실행되는 JavaScript에서 온 것임을 확신 할 수 있습니다.
이를 활용하려면 (CSRF Protection) 서버가 첫 번째 HTTP GET 요청에서 XSRF-TOKEN이라는 JavaScript 읽기 가능 세션 쿠키에 토큰을 설정해야합니다. 후속 비 GET 요청에서 서버는 쿠키가 X-XSRF-TOKEN HTTP 헤더와 일치하는지 확인할 수 있습니다.
다음은 해당 지침을 기반으로 한 솔루션입니다.
먼저 쿠키를 설정하십시오.
# 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
기본 Rails CSRF 보호 ( <%= csrf_meta_tags %>
)를 사용하는 경우 다음 과 같이 Angular 모듈을 구성 할 수 있습니다.
myAngularApp.config ["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
]
또는 CoffeeScript를 사용하지 않는 경우 (what !?) :
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
]
Also, be sure to check out HungYuHei's answer, which covers all the bases on the server rather than the client.
The angular_rails_csrf gem automatically adds support for the pattern described in HungYuHei's answer to all your controllers:
# Gemfile
gem 'angular_rails_csrf'
The answer that merges all previous answers and it relies that you are using Devise
authentication gem.
First of all, add the gem:
gem 'angular_rails_csrf'
Next, add rescue_from
block into 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
And the finally, add the interceptor module to you angular app.
# 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')
I saw the other answers and thought they were great and well thought out. I got my rails app working though with what I thought was a simpler solution so I thought I'd share. My rails app came with this defaulted in it,
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
I read the comments and it seemed like that is what I want to use angular and avoid the csrf error. I changed it to this,
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
And now it works! I don't see any reason why this shouldn't work, but I'd love to hear some insight from other posters.
I've used the content from HungYuHei's answer in my application. I found that I was dealing with a few additional issues however, some because of my use of Devise for authentication, and some because of the default that I got with my application:
protect_from_forgery with: :exception
I note the related stack overflow question and the answers there, and I wrote a much more verbose blog post that summarises the various considerations. The portions of that solution that are relevant here are, in the application controller:
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
I found a very quick hack to this. All I had to do is the following:
a. In my view, I initialize a $scope
variable which contains the token, let's say before the form, or even better at controller initialization:
<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">
b. In my AngularJS controller, before saving my new entry, I add the token to the hash:
$scope.addEntry = ->
$scope.newEntry.authenticity_token = $scope.authenticity_token
entry = Entry.save($scope.newEntry)
$scope.entries.push(entry)
$scope.newEntry = {}
Nothing more needs to be done.
angular
.module('corsInterceptor', ['ngCookies'])
.factory(
'corsInterceptor',
function ($cookies) {
return {
request: function(config) {
config.headers["X-XSRF-TOKEN"] = $cookies.get('XSRF-TOKEN');
return config;
}
};
}
);
It's working on angularjs side!
'Programing' 카테고리의 다른 글
PostgreSQL 9.2 pg_dump 버전 불일치 (0) | 2020.07.06 |
---|---|
Firebase 용 Cloud Functions를 구성하여 여러 파일에서 여러 기능을 배포하려면 어떻게해야하나요? (0) | 2020.07.06 |
IOS7 : UINavigationController의 UIScrollView 오프셋 (0) | 2020.07.05 |
PHP를 사용하여 페이지 새로 고침 (0) | 2020.07.05 |
사파리에서 유효하지 않은 날짜 (0) | 2020.07.05 |