[ruby-on-rails] 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
]
또한 HungYuHei의 답변 을 확인하십시오.이 답변 은 클라이언트가 아닌 서버의 모든 기반을 포괄합니다.
답변
angular_rails_csrf의 보석은 자동으로 설명 된 패턴에 대한 지원을 추가 HungYuHei의 대답은 모든 컨트롤러 :
# Gemfile
gem 'angular_rails_csrf'
답변
모든 이전 답변을 병합하고 Devise
인증 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
마지막으로 인터셉터 모듈을 각도 앱에 추가하십시오.
# 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 앱은 기본적으로 제공됩니다.
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
나는 주석을 읽었고 그것이 각도를 사용하고 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
답변
나는 이것에 대한 매우 빠른 해킹을 발견했다. 내가해야 할 일은 다음과 같습니다.
ㅏ. 내 관점에서, 나는 $scope
토큰을 포함 하는 변수를 초기화 하거나, 양식 전에 말하거나 심지어 컨트롤러 초기화에 더 잘합시다.
<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 = {}
더 이상 할 일이 없습니다.