복붙노트

[RUBY-ON-RAILS] 레일 고안 인증, CSRF 문제

RUBY-ON-RAILS

레일 고안 인증, CSRF 문제

나는 레일을 사용하여 그을음 페이지 응용 프로그램을하고 있어요. 유증 컨트롤러와 로그 아웃 할 때 Ajax를 사용하여 호출됩니다. 내가 갖는 문제는 내가 1) 2에 로그인 할 때) 다시 일을하지 않습니다에 다음 서명 로그 아웃이다.

나는 내가 로그 아웃 할 때 리셋을 얻을 수있는 토큰 CSRF에 관한 생각하고 한 페이지, 따라서 세션을 재설정 XHR 요청에서 전송되는 토큰 이전 CSRF 이후 (그렇지 AFAIK해야하지만).

이 워크 플로 더 구체적으로 :

모든 단서가 대단히 감사합니다! 나는 더 이상 세부 사항을 추가 할 수 있는지 알려주세요.

해결법

  1. ==============================

    1.짐보는 "왜"문제 뒤에 당신이로 실행중인 설명하는 멋진 일을했다. 이 문제를 해결하기 위해 취할 수있는 방법은 두 가지가 있습니다 :

    짐보는 "왜"문제 뒤에 당신이로 실행중인 설명하는 멋진 일을했다. 이 문제를 해결하기 위해 취할 수있는 방법은 두 가지가 있습니다 :

  2. ==============================

    2.난 그냥뿐만 아니라이 문제로 실행했습니다. 여기서 살펴 봐야 할 것들이 많다.

    난 그냥뿐만 아니라이 문제로 실행했습니다. 여기서 살펴 봐야 할 것들이 많다.

    TL; DR - 실패하는 이유는 서버 세션과 관련된 토큰 CSRF는 (당신이 로그인 또는 로그 아웃하고 있는지 서버 세션있어)이다. CSRF 토큰은 DOM의 모든 페이지로드에 페이지를 포함되어 있습니다. 로그 아웃, 세션이 재설정되고 더 CSRF 토큰이 없습니다. 일반적으로, 당신이 아약스를 사용하고 있기 때문에 당신에게 새로운 CSRF 토큰을 제공하지만, 다른 페이지 / 액션에 로그 아웃 리디렉션, 당신은 수동으로 수행해야합니다.

    자세한 설명 당신은 가장 가능성 (이것이 내가 생각하는 매우 일반적이다) 다른 컨트롤러의 모든 상속있는 당신의 ApplicationController.rb 파일에 protect_from_forgery 세트를 가지고있다. protect_from_forgery 수행하는 모든 비 GET은 HTML / 자바 스크립트 요청에 검사를 CSRF. 유증 로그인이 POST이기 때문에, 그것은 CSRF 검사를 수행합니다. CSRF 확인은 서버가 (올바른 / 원하는 동작입니다) 공격의 가정하기 때문에 사용자의 현재 세션, 즉, 사용자가 로그 아웃, 클리어에 실패합니다.

    A가 상태를 로그 아웃 당신이 시작하고 가정 그래서, 당신은 새로운 페이지로드를 수행하고 다시는 페이지를 다시로드 :

    업데이트 : 8/14 고안 로그 아웃, 당신에게 로그 아웃 당신에게 새로운 CSRF 토큰을 제공 한 후 일반적으로 발생하는 리디렉션을 새로운 CSRF 토큰을 제공하지 않습니다.

  3. ==============================

    3.내 대답은 그러나 나는 유증을 사용하고, @Jimbo 및 @Sija 모두에서 많이 차용 / 컨벤션 레일 CSRF 보호 + Angular.js에서 제안 AngularJS와 : protect_from_forgery가 POST에 로그 아웃 나를 만들고, 내 블로그에 조금 정교 때 원래 이런 짓을. 이 CSRF에 대한 설정 쿠키 응용 프로그램 컨트롤러의 방법이있다 :

    내 대답은 그러나 나는 유증을 사용하고, @Jimbo 및 @Sija 모두에서 많이 차용 / 컨벤션 레일 CSRF 보호 + Angular.js에서 제안 AngularJS와 : protect_from_forgery가 POST에 로그 아웃 나를 만들고, 내 블로그에 조금 정교 때 원래 이런 짓을. 이 CSRF에 대한 설정 쿠키 응용 프로그램 컨트롤러의 방법이있다 :

    after_filter  :set_csrf_cookie_for_ng
    
    def set_csrf_cookie_for_ng
      cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
    end
    

    내가 Sija의 형식 @ 사용하지만, 저를주고, 그 이전 SO 솔루션의 코드를 사용하고 그래서 :

    class SessionsController < Devise::SessionsController
      after_filter :set_csrf_headers, only: [:create, :destroy]
    
      protected
      def set_csrf_headers
        cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?  
      end
    end
    

    그것을 해결하기 위해 나에게 몇 분 걸렸다 때문에 완성도를 들어, 나는 또한 당신이 세션 컨트롤러를 무시했다고 선언하기 위해 설정 / routes.rb 수정할 필요가 있습니다. 뭔가 같은 :

    devise_for :users, :controllers => {sessions: 'sessions'}
    

    이것은 또한 다른 사람에게 흥미로운 일이 될 수도 있습니다 내 응용 프로그램에서 수행 한 것을 큰 CSRF 정리의 일부였다. 블로그 포스트는 여기에, 다른 변경 사항은 다음과 같습니다

    일이 동기화의 나갈 경우 응용 프로그램이 아니라 명확한 쿠키 필요로하는 사용자에 비해, 자체 해결된다는 것을 의미합니다 ActionController :: InvalidAuthenticityToken에서 구출. 상황이 레일에 서서 나는 응용 프로그램 컨트롤러로 디폴트 될 것이라고 생각 :

    protect_from_forgery with: :exception
    

    그 상황에서는, 당신은 필요 :

    rescue_from ActionController::InvalidAuthenticityToken do |exception|
      cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
      render :error => 'invalid token', {:status => :unprocessable_entity}
    end
    

    당신이 cookie_store보다는 active_record_store 사용을 고려하고, 병렬 발행주의해야 짧은 - 나는 또한 경쟁 조건 내가 블로그 게시물에서 더에 댓글을 한 유증에 timeoutable 모듈, 일부 상호 작용 약간의 슬픔을 했어 sign_in 및 sign_out 행동 근처에 요청.

  4. ==============================

    4.이것은 필자의 의견입니다 :

    이것은 필자의 의견입니다 :

    class SessionsController < Devise::SessionsController
      after_filter :set_csrf_headers, only: [:create, :destroy]
      respond_to :json
    
      protected
      def set_csrf_headers
        if request.xhr?
          response.headers['X-CSRF-Param'] = request_forgery_protection_token
          response.headers['X-CSRF-Token'] = form_authenticity_token
        end
      end
    end
    

    그리고 클라이언트 측 :

    $(document).ajaxComplete(function(event, xhr, settings) {
      var csrf_param = xhr.getResponseHeader('X-CSRF-Param');
      var csrf_token = xhr.getResponseHeader('X-CSRF-Token');
    
      if (csrf_param) {
        $('meta[name="csrf-param"]').attr('content', csrf_param);
      }
      if (csrf_token) {
        $('meta[name="csrf-token"]').attr('content', csrf_token);
      }
    });
    

    당신의 CSRF 메타 태그는 아약스 요청을 통해 X-CSRF 토큰 또는 X-CSRF - 파람 헤더를 반환 할 때마다 계속 업데이트됩니다 어느.

  5. ==============================

    5.교도소 소스에 파고 후, 나는 CSRF 토큰이 서명 아웃 사이에 유지되도록 false로 sign_out_all_scopes을 설정하면, 전체 세션을 삭제에서 소장을 중지 것으로 나타났습니다.

    교도소 소스에 파고 후, 나는 CSRF 토큰이 서명 아웃 사이에 유지되도록 false로 sign_out_all_scopes을 설정하면, 전체 세션을 삭제에서 소장을 중지 것으로 나타났습니다.

    유증 발행 테커에 관련 토론 : https://github.com/plataformatec/devise/issues/2200

  6. ==============================

    6.난 그냥 내 레이아웃 파일이 추가하고 일했다

    난 그냥 내 레이아웃 파일이 추가하고 일했다

        <%= csrf_meta_tag %>
    
        <%= javascript_tag do %>
          jQuery(document).ajaxSend(function(e, xhr, options) {
           var token = jQuery("meta[name='csrf-token']").attr("content");
            xhr.setRequestHeader("X-CSRF-Token", token);
          });
        <% end %>
    
  7. ==============================

    7.당신이 당신의 application.js 파일이 포함 여부 확인

    당신이 당신의 application.js 파일이 포함 여부 확인

    이유 존재는 자동으로 기본적으로 모든 Ajax 요청에 토큰 CSRF를 설정 보석 JQuery와 - 레일입니다 두를 필요

  8. ==============================

    8.내 경우에 사용자가 로그인 한 후, 나는 사용자의 메뉴를 다시 그릴 필요가 있었다. 즉 일,하지만 난, 같은 섹션에 (물론, 페이지를 새로 고치지 않고) 서버에 대한 모든 요청에 ​​CSRF의 인증 오류를 얻었다. 내가 JS보기를 렌더링하는 데 필요하기 때문에 위의 솔루션이 작동되지 않았습니다.

    내 경우에 사용자가 로그인 한 후, 나는 사용자의 메뉴를 다시 그릴 필요가 있었다. 즉 일,하지만 난, 같은 섹션에 (물론, 페이지를 새로 고치지 않고) 서버에 대한 모든 요청에 ​​CSRF의 인증 오류를 얻었다. 내가 JS보기를 렌더링하는 데 필요하기 때문에 위의 솔루션이 작동되지 않았습니다.

    내가 한 일은이 사용 고안입니다 :

    응용 프로그램 / 컨트롤러 / sessions_controller.rb

       class SessionsController < Devise::SessionsController
          respond_to :json
    
          # GET /resource/sign_in
          def new
            self.resource = resource_class.new(sign_in_params)
            clean_up_passwords(resource)
            yield resource if block_given?
            if request.format.json?
              markup = render_to_string :template => "devise/sessions/popup_login", :layout => false
              render :json => { :data => markup }.to_json
            else
              respond_with(resource, serialize_options(resource))
            end
          end
    
          # POST /resource/sign_in
          def create
            if request.format.json?
              self.resource = warden.authenticate(auth_options)
              if resource.nil?
                return render json: {status: 'error', message: 'invalid username or password'}
              end
              sign_in(resource_name, resource)
              render json: {status: 'success', message: '¡User authenticated!'}
            else
              self.resource = warden.authenticate!(auth_options)
              set_flash_message(:notice, :signed_in)
              sign_in(resource_name, resource)
              yield resource if block_given?
              respond_with resource, location: after_sign_in_path_for(resource)
            end
          end
    
        end
    

    그 후 나는 메뉴를 묘화 제어부 # 1 액션 요청했다. 그리고 자바 스크립트에서, 나는 X-CSRF - 파람 및 X-CSRF 토큰을 수정 :

    응용 프로그램 / 뷰 / 유틸리티 / redraw_user_menu.js.erb

      $('.js-user-menu').html('');
      $('.js-user-menu').append('<%= escape_javascript(render partial: 'shared/user_name_and_icon') %>');
      $('meta[name="csrf-param"]').attr('content', '<%= request_forgery_protection_token.to_s %>');
      $('meta[name="csrf-token"]').attr('content', '<%= form_authenticity_token %>');
    

    나는 그것이 같은 JS 상황에서 누군가를 위해 유용 희망 :)

  9. ==============================

    9.내 상황은 더 간단했다. 사람이 양식 화면에 앉아 자신의 세션 시간 초과 (고안 timeoutable 세션 시간 초과) 경우, 일반적으로 그들은 그 시점에 제출 치면, 유증이 다시 반송 것 : 내 경우에는, 모든 나는이되었다하고 싶어 로그인 화면으로 이동합니다. 그들은 모든 형태의 데이터가 손실 때문에 글쎄, 나는 그것을 원하지 않았다. 나는 양식, 아약스, 사용자가 더 이상 로그인하면 결정하지 않는 컨트롤러를 호출 제출 잡기 위해 자바 스크립트를 사용하고 그런 경우가 있다면 나는 그들이 자신의 암호를 다시 입력 양식을 올려, 나는 (컨트롤러에 bypass_sign_in)을 재 인증 Ajax 호출을 사용하여. 그런 다음 원래의 형태는 계속 허용됩니다 제출합니다.

    내 상황은 더 간단했다. 사람이 양식 화면에 앉아 자신의 세션 시간 초과 (고안 timeoutable 세션 시간 초과) 경우, 일반적으로 그들은 그 시점에 제출 치면, 유증이 다시 반송 것 : 내 경우에는, 모든 나는이되었다하고 싶어 로그인 화면으로 이동합니다. 그들은 모든 형태의 데이터가 손실 때문에 글쎄, 나는 그것을 원하지 않았다. 나는 양식, 아약스, 사용자가 더 이상 로그인하면 결정하지 않는 컨트롤러를 호출 제출 잡기 위해 자바 스크립트를 사용하고 그런 경우가 있다면 나는 그들이 자신의 암호를 다시 입력 양식을 올려, 나는 (컨트롤러에 bypass_sign_in)을 재 인증 Ajax 호출을 사용하여. 그런 다음 원래의 형태는 계속 허용됩니다 제출합니다.

    내가 protect_from_forgery을 추가 할 때까지 완벽하게 작동했다.

    난 그냥 새로운 CSRF 토큰에 인스턴스 변수를 설정합니다 (bypass_sign_in)에서 사용자의 뒷면에 서명 어디 그래서, 모두 내가 필요로 위의 답변 덕분에 정말 내 컨트롤러에 있었다 :

    @new_csrf_token = form_authenticity_token
    

    그리고 렌더링 된 .js.erb에 (다시 보낸, 이것은 XHR 호출이었다)

    $('meta[name="csrf-token"]').attr('content', '<%= @new_csrf_token %>');
    $('input[type="hidden"][name="authenticity_token"]').val('<%= @new_csrf_token %>');
    

    짜잔. 따라서 갱신 및되지 않은 내 양식 페이지는, 지금은 내 사용자 로그인에서 가져온 새로운 세션에서 새 토큰을 가지고, 기존의 토큰으로 붙어 있었다.

  10. ==============================

    10.@ sixty4bit의 코멘트에 회신; 이 오류로 실행하면 :

    @ sixty4bit의 코멘트에 회신; 이 오류로 실행하면 :

    Unexpected error while processing request: undefined method each for :authenticity_token:Symbol` 
    

    바꾸다

    response.headers['X-CSRF-Param'] = request_forgery_protection_token
    

    response.headers['X-CSRF-Param'] = request_forgery_protection_token.to_s
    
  11. from https://stackoverflow.com/questions/11845500/rails-devise-authentication-csrf-issue by cc-by-sa and MIT license