rails-json 고안 요청에 대한 "경고 : CSRF 토큰 인증을 확인할 수 없습니다"


82

JSON 요청으로 전달할 CSRF 토큰을 검색하려면 어떻게해야합니까?

보안상의 이유로 Rails가 모든 요청 유형 (JSON / XML 포함) 에서 CSRF 토큰 을 확인하고 있다는 것을 알고 있습니다.

컨트롤러에 넣을 수는 skip_before_filter :verify_authenticity_token있지만 CRSF 보호 기능을 잃게됩니다 (권장하지 않음 :-)).

이 비슷한 (여전히 받아 들여지지 않음) 대답 은 다음을 제안합니다.

다음을 사용하여 토큰 검색 <%= form_authenticity_token %>

문제는 어떻게? 토큰을 검색하기 위해 내 페이지 중 하나를 먼저 호출 한 다음 Devise로 실제 인증을 수행해야합니까? 또는 내 서버에서 가져온 다음 지속적으로 사용할 수있는 일회성 정보입니까 (서버 자체에서 수동으로 변경할 때까지)?

답변:


127

편집 :

Rails 4에서는 아래 주석에서 @genkilabs가 제안한 것을 사용합니다.

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }

내장 된 보안을 완전히 끄는 대신 CSRF 토큰없이 서버에 무언가가 도달했을 때 존재할 수있는 모든 세션을 종료합니다.


skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }

이렇게하면 적절하게 표시된 json 게시물 / put에 대한 CSRF 검사가 해제됩니다.

예를 들어 iOS에서 "parameters"가 매개 변수 인 NSURLRequest에 다음을 설정합니다.


[request setHTTPMethod:@"POST"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"content-type"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"accept"];

[request setHTTPBody:[NSData dataWithBytes:[parameters UTF8String] 
                                            length:[parameters length]]];

3
이렇게하면 공격자가 형식을 'json'으로 표시하는 경우 공격에 노출됩니다. 이것이 Rails가 정의에 따라 경고를 발행하는 이유입니다. 내 json이 토큰을 제대로 전달할 수 있기를 원합니다. 그렇지 않으면 로그에 경고가 표시되는 것이 좋습니다.

21
공격에 노출되기 때문에 필터를 끌 때 다른 보안을 마련 할 것을 권장합니다. 일반적으로 API 요청의 경우 요청자는 게시 데이터와 함께 API 키를 전송하여 원하는 메서드를 실행하기 전에 확인합니다.
— Ryan Crews 2012

18
rails 4에서는 다음과 같이 할 수 있습니다 :protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
— genkilabs 2013-08-30

2
그런 @genkilabs 나는, 4 사용자, 감사 =] 레일에 대한 대답에 추가
— 라이언 대원에게

1
Rails 및 iOS 앱에서이 작업을 수행하기 위해 방금 두 가지 작업을 수행했습니다. 1) Ruby에서 이전 스타일과 새 스타일의 해시 구문을 혼합 한 이유를 알 수 없었으므로 컨트롤러의 코드는 다음과 같습니다. protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2)이 질문과는 관련이 없지만 iOS에서 POSTing JSON 주제와 관련이 있습니다. 사용 AFNetworkingmanager.requestSerializer = [AFJSONRequestSerializer serializer];
— 중이고

18

성공적으로 로그인 한 후 사용자 지정 헤더를 사용하여 CSRF 토큰을 보낼 수 있습니다.

예를 들어, 이것을 세션에 넣으십시오.

response.headers['X-CSRF-Token'] = form_authenticity_token

CSRF 토큰을 제공하는 샘플 로그인 응답 헤더 :

HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 35
Content-Type: application/json; charset=utf-8
Date: Mon, 22 Oct 2012 11:39:04 GMT
Etag: "9d719d3b9aabd413c3603e04e8a3933d"
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Set-Cookie: [cut for readability] 
X-Csrf-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=
X-Request-Id: 178746992d7aca928c876818fcdd4c96
X-Runtime: 0.169792
X-Ua-Compatible: IE=Edge

이 토큰은 다시 로그인하거나 (API를 통해 지원하는 경우 로그 아웃 할 때까지) 유효합니다. 클라이언트는 로그인 응답 헤더에서 토큰을 추출하고 저장할 수 있습니다. 그런 다음 각 POST / PUT / DELETE 요청은 로그인시받은 값으로 X-CSRF-Token 헤더를 설정해야합니다.

CSRF 토큰이있는 샘플 POST 헤더 :

POST /api/report HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate, compress
Content-Type: application/json; charset=utf-8
Cookie: [cut for readability]
Host: localhost:3000
User-Agent: HTTPie/0.3.0
X-CSRF-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=

문서 : form_authenticity_token


18

정말 가장 간단한 방법입니다. 헤더 변경에 신경 쓰지 마십시오.

다음을 확인하십시오.

<%= csrf_meta_tag %>

당신의 layouts/application.html.erb

다음과 같이 숨겨진 입력 필드를 수행하십시오.

<input name="authenticity_token" 
       type="hidden" 
       value="<%= form_authenticity_token %>"/>

또는 jquery ajax 게시물을 원하는 경우 :

$.ajax({     
    type: 'POST',
    url: "<%= someregistration_path %>",
    data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },                                                                                  
    error: function( xhr ){ 
      alert("ERROR ON SUBMIT");
    },
    success: function( data ){ 
      //data response can contain what we want here...
      console.log("SUCCESS, data="+data);
    }
});

기본적으로 json 데이터를 게시 할 때 유효한 authenticity_token 필드를 데이터에 추가 post하면 경고가 사라집니다.


IMHO 로거에 경고를 표시 할뿐만 아니라 기본적으로 유효한 csrf 태그가없는 게시물을 완전히 폐기해야합니다. 기본적으로 경고를 게시하고 데이터를
— 제대로

숨겨진 인증 토큰 입력 필드를 수행하는 더 짧은 방법은 = token_tag(nil)(nil이 중요합니다)
— Yo Ludke

이전 버전의 레일에서는 태그가 복수로 끝납니다. <% = csrf_meta_tags %>
— cyonder

4

이 방법으로 오류를 해결했습니다.

class ApplicationController < ActionController::Base
  protect_from_forgery
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected

  def json_request?
    request.format.json?
  end
end

출처 : http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html


이 작업을 원하는지 잘 모르겠습니다. 브라우저는 JSON API를 사용할 수 있습니다.
— Joe Van Dyk

감사합니다! 이 문제를 해결하기 위해 빠르고 더러운 방법이 필요했으며 이것은 내가 달성하려는 것에 대해 충분히 잘 작동 할 수 있습니다.
— NerdyTherapist

링크 된 문서 (현재 Rails 5 용으로 업데이트 됨)는 다음 protect_from_forgery with: :exception, unless: -> { request.format.json? }과 같이 보입니다 . 감사.
— akostadinov

1
이것은 문제를 방지하는 검사를 비활성화합니다. 앱이이 기능을 비활성화한다면 위조 방지 기능이 켜져있는 이유는 무엇입니까?
— jefflunt

3

걱정되는 것은 Rails 3.2.3에서 이제 production.log에 CSRF 경고가 표시되지만 게시가 실패하지 않는다는 것입니다! 공격으로부터 나를 보호하므로 실패하기를 원합니다. 필터 btw 전에 jquery를 사용하여 csrf 토큰을 추가 할 수 있습니다.

http://jasoncodes.com/posts/rails-csrf-vulnerability


2

나는 아래를 사용했습니다. 포함 사용? 따라서 콘텐츠 유형이 application / json; charset = utf-8이면 여전히 작동합니다.

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format.include? 'application/json' }

2

이 대답 이 더 좋습니다.

XMLHttpRequest를 보내기 전에 추가 노력없이 (토큰이 추가됨) CSRF-TOKEN 유효성 검사를 유지할 수 있습니다. JQuery도없고, 복사 / 붙여 넣기 및 새로 고침 만 수행 할 수 없습니다.

이 코드를 추가하기 만하면됩니다.

(function() {
    var send = XMLHttpRequest.prototype.send,
        token = $('meta[name=csrf-token]').attr('content');
    XMLHttpRequest.prototype.send = function(data) {
        this.setRequestHeader('X-CSRF-Token', token);
        return send.apply(this, arguments);
    };
}());

1

다음 버전의 Rails에서 동일한 문제가 발생했습니다 :
gem 'rails', : git => 'git : //github.com/rails/rails.git', : branch => '3-2-stable'

나는 3.2.2로 업데이트했고 모든 것이 잘 작동합니다. :)
gem 'rails', '3.2.2'


제안 해 주셔서 감사합니다. 나는 그것을 밖으로 시도,하지만 난 여전히 같은 경고가WARNING: Can't verify CSRF token authenticity

0

오늘 밤 같은 문제가 발생했습니다. 그 이유는 마지막 csrf-token에 로그인 할 때 더 이상 유효하지 않기 때문입니다. 내가 한 일은 : $("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>'); 당신의 app / views / devise / sessions / create.js.rb에있었습니다.

이제 유효한 csrf-token이 있습니다. :) 도움이되기를 바랍니다.


0

또한 개발 / 테스트 모드 용입니다.

protect_from_forgery with: :exception unless %w(development test).include? Rails.env

이 경고 :null_session는를 사용 하고 있기 때문에 표시되며 , Rails 4.1에서는 with:지정된 옵션 이없는 경우 기본적으로 작동 합니다.

protect_from_forgery
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.