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.