protect_from_forgery
オプションがapplication_controllerで言及されている場合、ログインしてGETリクエストを実行できますが、最初のPOSTリクエストでRailsがセッションをリセットし、ログアウトします。
このprotect_from_forgery
オプションを一時的にオフにしましたが、Angular.jsで使用したいと思います。それを行う方法はありますか?
protect_from_forgery
オプションがapplication_controllerで言及されている場合、ログインしてGETリクエストを実行できますが、最初のPOSTリクエストでRailsがセッションをリセットし、ログアウトします。
このprotect_from_forgery
オプションを一時的にオフにしましたが、Angular.jsで使用したいと思います。それを行う方法はありますか?
回答:
DOMからCSRF値を読み取ることは良い解決策ではないと思います。これは単なる回避策です。
これは、angularJS公式ウェブサイトのドキュメント形式ですhttp://docs.angularjs.org/api/ng.$http :
ドメインで実行されているJavaScriptのみがCookieを読み取ることができるため、サーバーでXHRがドメインで実行されているJavaScriptからのものであることが保証されます。
これ(CSRF保護)を利用するには、サーバーは最初のHTTP GETリクエストでXSRF-TOKENと呼ばれるJavaScript読み取り可能なセッションCookieにトークンを設定する必要があります。後続の非GET要求で、サーバーはCookieがX-XSRF-TOKEN 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
デフォルトのRails CSRF保護(<%= csrf_meta_tags %>
)を使用している場合は、次のように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の回答も必ず確認してください。
<%= csrf_meta_tags %>
。言及するprotect_from_forgery
だけで十分だと思いました。何をすべきか?ベースドキュメントはプレーンHTMLである必要があります(私はここでは選択しません)。
protect_from_forgery
、あなたが言っている「私のJavaScriptコードは、Ajaxリクエストを行ったとき、私が送信することを約束されたX-CSRF-Token
現在のCSRFトークンに対応することをヘッダー内に。」このトークンを取得するために、RailsはトークンをDOMに挿入し、<%= csrf_meta_token %>
Ajaxリクエストを行うたびにjQueryでメタタグのコンテンツを取得します(デフォルトのRails 3 UJSドライバーがこれを行います)。ERBを使用していない場合、RailsからページやJavaScriptに現在のトークンを取得する方法がないためprotect_from_forgery
、この方法では使用できません。
csrf_meta_tags
、サーバーが応答を生成するたび、およびこれらのタグが以前のものとは異なるたびにクライアント側が受信すると私が思ったこと。したがって、これらのタグはリクエストごとに一意です。問題は、AJAXリクエスト(角度なし)のアプリケーションがこれらのタグをどのように受信するかです。私はjQuery POSTリクエストでprotect_from_forgeryを使用しましたが、このCSRFトークンを取得することに煩わされることはありませんでした。どうやって?
jQuery.ajaxPrefilter
ここに示されているよう:github.com/indirect/jquery-rails/blob/c1eb6ae/vendor/assets/...は、このファイルを熟読し、すべてのRailsがしなくて、それはかなり動作させるために経由ジャンプフープ見ることができますそれについて心配してください。
put
ではpost
なくオンにするだけで意味がないのではないでしょうcommon
か?レールセキュリティガイドから:The solution to this is including a security token in non-GET requests
angular_rails_csrf宝石が自動的に記述パターンのサポート追加HungYuHeiの答えを、すべてのコントローラに:
# Gemfile
gem 'angular_rails_csrf'
angular_rails_csrf
gemはRails 5では機能しません。ただし、CSRFメタタグの値を使用してAngularリクエストヘッダーを構成すると機能します。
以前のすべての回答をマージし、Devise
認証gem を使用していることに依存しています。
まず、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')
$injector
直接注入するのではなく、なぜ注入するの$http
ですか?
私は他の答えを見て、それらが素晴らしくてよく考えられていると思いました。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。私の見解$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側で動作しています!