CSRF सुरक्षा + Angular.js: रेलिंग_फ्रॉम_फॉर्गी मुझे POST पर लॉग आउट करने के लिए बनाती है


129

यदि protect_from_forgeryविकल्प में application_controller का उल्लेख है, तो मैं लॉग इन कर सकता हूं और कोई भी GET अनुरोध कर सकता हूं, लेकिन पहले POST अनुरोध पर रेल सत्र को रीसेट करता है, जो मुझे लॉग आउट करता है।

मैंने protect_from_forgeryविकल्प को अस्थायी रूप से बंद कर दिया , लेकिन इसका उपयोग Angular.js के साथ करना चाहूंगा। क्या ऐसा करने का कोई तरीका है?


देखें कि क्या इससे कोई मदद मिलती है, इसके बारे में HTTP हेडर stackoverflow.com/questions/14183025/…
मार्क राजकोक

जवाबों:


276

मुझे लगता है कि DOM से CSRF-value पढ़ना एक अच्छा समाधान नहीं है, यह सिर्फ एक समाधान है।

यहाँ एक दस्तावेज़ प्रपत्र angularJS की आधिकारिक वेबसाइट http://docs.angularjs.org/api/ng.$http :

चूंकि केवल जावास्क्रिप्ट जो आपके डोमेन पर चलता है, कुकी को पढ़ सकता है, आपके सर्वर को आश्वासन दिया जा सकता है कि आपके डोमेन पर चलने वाले जावास्क्रिप्ट से XHR आया था।

इसका (CSRF सुरक्षा) लाभ उठाने के लिए, आपके सर्वर को पहले HTTP GET अनुरोध पर XSRF-TOKEN नामक जावास्क्रिप्ट पठनीय सत्र कुकी में टोकन सेट करने की आवश्यकता है। बाद में गैर-जीईटी अनुरोधों पर सर्वर सत्यापित कर सकता है कि कुकी 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

फिर, हमें हर गैर-जीईटी अनुरोध पर टोकन को सत्यापित करना चाहिए।
चूंकि रेल पहले से ही इसी तरह की विधि के साथ बनाई गई है, हम अपने तर्क को जोड़ने के लिए बस इसे ओवरराइड कर सकते हैं:

# 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

18
मुझे यह तकनीक पसंद है, क्योंकि आपको किसी भी क्लाइंट-साइड कोड को संशोधित करने की आवश्यकता नहीं है।
मिशेल टाइली

11
यह समाधान CSRF सुरक्षा की उपयोगिता को कैसे बनाए रखता है? कुकी सेट करके, चिह्नित उपयोगकर्ता का ब्राउज़र क्रॉस-साइट अनुरोधों सहित बाद के सभी अनुरोधों पर उस कुकी को भेज देगा। मैं एक दुर्भावनापूर्ण तृतीय पक्ष साइट सेट कर सकता हूं जो दुर्भावनापूर्ण अनुरोध भेजती है और उपयोगकर्ता का ब्राउज़र सर्वर पर 'XSRF-TOKEN' भेजेगा। ऐसा लगता है कि यह समाधान पूरी तरह से सीएसआरएफ संरक्षण को बंद करने के लिए समान है।
स्टीवन

9
कोणीय डॉक्स से: "चूंकि केवल जावास्क्रिप्ट जो आपके डोमेन पर चलता है, कुकी को पढ़ सकता है, आपके सर्वर को आश्वासन दिया जा सकता है कि आपके डोमेन पर चलने वाले जावास्क्रिप्ट से XHR आया था।" @StevenXu - तीसरी पार्टी साइट कुकी कैसे पढ़ेगी?
जिमी बेकर

8
@ जिमीबैकर: हाँ, आप सही कह रहे हैं। मैंने प्रलेखन की समीक्षा की है। दृष्टिकोण वैचारिक रूप से ध्वनि है। मैंने कुकी की सेटिंग को मान्यता के साथ भ्रमित किया, यह महसूस करते हुए कि कोणीय रूपरेखा कुकी के मूल्य के आधार पर एक कस्टम हेडर सेट कर रही थी!
स्टीवन

5
form_authenticity_token रेल 4.2 में प्रत्येक कॉल पर नए मान उत्पन्न करता है, इसलिए यह अब काम नहीं करता है।
डेव

78

यदि आप डिफ़ॉल्ट रेल सीएसआरएफ सुरक्षा ( <%= 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');
  }
]);

यदि आप पसंद करते हैं, तो आप शीर्ष लेख को केवल गैर-जीईटी अनुरोधों पर भेज सकते हैं, जैसे कि निम्नलिखित कुछ:

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 के उत्तर की जांच करना सुनिश्चित करें , जो क्लाइंट के बजाय सर्वर पर सभी ठिकानों को कवर करता है।


मुझे समझाने दो। आधार दस्तावेज़ एक सादा HTML है, नहीं। इसलिए मैं उपयोग नहीं कर सकता <%= csrf_meta_tags %>। मैंने सोचा कि protect_from_forgeryकेवल उल्लेख करने के लिए पर्याप्त होना चाहिए । क्या करें? आधार दस्तावेज़ एक सादा HTML होना चाहिए (मैं यहाँ हूँ जो चुनने वाला नहीं है)।
पॉल

3
जब आप protect_from_forgeryयह कहते हैं कि आप क्या कह रहे हैं "जब मेरा जावास्क्रिप्ट कोड अजाक्स अनुरोध करता है, तो मैं X-CSRF-Tokenउस हेडर में भेजने का वादा करता हूं जो वर्तमान सीएसआरएफ टोकन से मेल खाता है।" इस टोकन को प्राप्त करने के लिए, रेल इसे DOM में इंजेक्ट करती है <%= csrf_meta_token %>और जब भी यह Ajax अनुरोध करता है, तब jQuery के साथ मेटा टैग की सामग्री प्राप्त करता है (डिफ़ॉल्ट रेल 3 UJS ड्राइवर आपके लिए ऐसा करता है)। यदि आप ईआरबी का उपयोग नहीं कर रहे हैं, तो रेल से पृष्ठ और / या जावास्क्रिप्ट में वर्तमान टोकन प्राप्त करने का कोई तरीका नहीं है - और इस तरह आप protect_from_forgeryइस तरीके का उपयोग नहीं कर सकते ।
मिशेल टिली

समझाने के लिए धन्यवाद। मुझे क्या लगा कि क्लासिक सर्वर-साइड एप्लिकेशन में क्लाइंट पक्ष csrf_meta_tagsहर बार सर्वर द्वारा एक प्रतिक्रिया उत्पन्न करता है, और हर बार ये टैग पिछले वाले से अलग होते हैं। इसलिए, ये टैग प्रत्येक अनुरोध के लिए अद्वितीय हैं। सवाल यह है: आवेदन AJAX अनुरोध (कोणीय के बिना) के लिए ये टैग कैसे प्राप्त करता है? मैंने jQuery POST अनुरोधों के साथ protect_from_forgery का उपयोग किया, इस CSRF टोकन को प्राप्त करने के साथ खुद को कभी परेशान नहीं किया और यह काम किया। कैसे?
पॉल

1
रेल यूजेएस ड्राइवर का उपयोग jQuery.ajaxPrefilterयहां दिखाया गया है: github.com/indirect/jquery-rails/blob/c1eb6ae/vendor/assets/… आप इस फाइल को मना कर सकते हैं और सभी खुरों को देख सकते हैं रेल के माध्यम से कूदता है यह बिना काम किए बिना बहुत ज्यादा चलता है। इसके बारे में चिंता करें।
मिशेल टिली

@BrandonTilley इसका मतलब यह नहीं होगा कि यह केवल पर putऔर postइसके बजाय ऐसा करते हैं common? से रेल सुरक्षा गाइड :The solution to this is including a security token in non-GET requests
christianvuerings

29

Angular_rails_csrf मणि स्वचालित रूप से पैटर्न में वर्णित के लिए समर्थन जोड़ता HungYuHei का जवाब अपने सभी नियंत्रकों के लिए:

# Gemfile
gem 'angular_rails_csrf'

किसी भी विचार कैसे आप सही ढंग से कोणीय_rails_css का उपयोग करने के लिए अपने आवेदन नियंत्रक और अन्य csrf / जालसाजी से संबंधित सेटिंग्स कॉन्फ़िगर करना चाहिए?
बेन व्हीलर

इस टिप्पणी के समय angular_rails_csrfमणि रेल 5 के साथ काम नहीं करता है। हालांकि, सीएसआरएफ मेटा टैग कार्यों से मूल्य के साथ कोणीय अनुरोध हेडर को कॉन्फ़िगर करना!
bideowego

मणि की एक नई रिलीज़ है, जो रेल्स 5 का समर्थन करती है
15

4

वह उत्तर जो पिछले सभी उत्तरों को मिलाता है और यह निर्भर करता है कि आप Deviseप्रमाणीकरण रत्न का उपयोग कर रहे हैं ।

सबसे पहले, मणि जोड़ें:

gem 'angular_rails_csrf'

अगला, rescue_fromapplication_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')

1
आप $injectorसीधे सीधे इंजेक्शन लगाने के बजाय इंजेक्शन क्यों दे रहे हैं $http?
व्हाइटहाट १०

यह काम करता है, लेकिन केवल मुझे लगता है कि अगर पहले से ही अनुरोध दोहराया गया है तो मैं जांच कर रहा हूं। जब यह दोहराया गया तो हम फिर से नहीं भेजेंगे क्योंकि यह हमेशा के लिए लूप कर देगा।
duleorlovic

1

मैंने अन्य उत्तर देखे और सोचा कि वे महान थे और अच्छी तरह से सोचा था। मैं अपने रेल एप्लिकेशन को काम कर रहा था, हालांकि मुझे लगा कि मैं एक सरल समाधान था इसलिए मैंने सोचा कि मैं साझा करूंगा। मेरा रेल ऐप इस डिफ़ॉल्ट के साथ आया,

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

मैंने टिप्पणियों को पढ़ा और ऐसा लगा कि मैं कोणीय का उपयोग करना चाहता हूं और सीएसआरएफ त्रुटि से बचना चाहता हूं। मैंने इसे इसे बदल दिया,

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

और अब यह काम करता है! मुझे कोई कारण नहीं दिखता है कि यह क्यों नहीं होना चाहिए, लेकिन मुझे अन्य पोस्टरों से कुछ अंतर्दृष्टि सुनना अच्छा लगेगा।


6
यह समस्याएँ पैदा करेगा यदि आप रेल के सत्रों का उपयोग करने की कोशिश कर रहे हैं क्योंकि यह जाल सेट करने के लिए सेट हो जाएगा यदि यह जालसाजी परीक्षण में विफल रहता है, जो हमेशा रहेगा, क्योंकि आप क्लाइंट की ओर से सीएसआरएफ-टोकन नहीं भेज रहे हैं।
हज्जप

लेकिन अगर आप रेल सत्र का उपयोग नहीं कर रहे हैं तो सब ठीक है; धन्यवाद! मैं सबसे साफ समाधान खोजने के लिए संघर्ष कर रहा हूं।
मॉर्गन

1

मैंने अपने आवेदन में HungYuHei के उत्तर से सामग्री का उपयोग किया है। मैंने पाया कि मैं कुछ अतिरिक्त मुद्दों के साथ काम कर रहा था, कुछ क्योंकि प्रमाणीकरण के लिए डेविस के मेरे उपयोग के कारण, और कुछ डिफ़ॉल्ट के कारण जो मुझे अपने आवेदन के साथ मिला:

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

1

मुझे इसके लिए बहुत तेज हैक मिली। मुझे केवल इतना करना था:

ए। मेरे विचार में, मैं एक $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 = {}

ज्यादा कुछ करने की जरूरत नहीं है।


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

यह angularjs की तरफ काम कर रहा है!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.