Rails : POST 요청을 할 때 CSRF 토큰 진위 여부를 확인할 수 없습니다.


89

POST request내 로컬 개발자에게 다음과 같이 만들고 싶습니다.

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

그러나 서버 콘솔에서보고합니다.

Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
  ActiveRecord::SchemaMigration Load (0.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
  Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

여기 내 컨트롤러와 경로 설정이 있습니다. 매우 간단합니다.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

무엇을해야하는지 잘 모르겠습니까? CSRF를 끄는 것은 확실히 효과가 있지만 그러한 API를 만들 때 내 실수라고 생각합니다.

수행해야하는 다른 설정이 있습니까?


5
API의 경우 일반적으로 CSRF 토큰 유효성 검사 를 해제하는 것이 허용됩니다 . 나는 protect_from_forgery with: :null_session.
dcestari

답변:


110

크로스 사이트 요청 위조 (CSRF / XSRF)는 악의적 인 웹 페이지가 북마크릿, iframe을 사용하거나 사용자를 속일 정도로 시각적으로 유사한 페이지를 만드는 등 의도하지 않은 요청을 수행하도록 사용자를 속이는 것입니다.

레일 CSRF 보호 "고전"웹 응용 프로그램을 위해 만들어 - 단순히 요청이 자신의 웹 응용 프로그램에서 유래 보증의 정도를 제공합니다. CSRF 토큰은 서버 만 알고있는 비밀처럼 작동합니다. Rails는 임의의 토큰을 생성하여 세션에 저장합니다. 양식은 숨겨진 입력을 통해 토큰을 보내고 Rails는 GET이 아닌 요청에 세션에 저장된 것과 일치하는 토큰이 포함되어 있는지 확인합니다.

그러나 API는 일반적으로 사이트 간 정의에 따라 웹 앱보다 더 많이 사용되는 것으로, CSRF의 전체 개념이 적용되지 않는다는 의미입니다.

대신 API 키와 시크릿으로 API 요청을 인증하는 토큰 기반 전략을 사용해야합니다. 요청이 자체 앱이 아닌 승인 된 API 클라이언트에서 온 것인지 확인하기 때문입니다.

@dcestari에서 지적한대로 CSRF를 비활성화 할 수 있습니다.

class ApiController < ActionController::Base
  protect_from_forgery with: :null_session
end

업데이트되었습니다. Rails 5에서는 다음 --api옵션 을 사용하여 API 전용 애플리케이션을 생성 할 수 있습니다 .

rails new appname --api

CSRF 미들웨어와 수퍼 플로우 인 다른 많은 구성 요소는 포함되지 않습니다.


감사합니다. CSRF의 일부를 해제하기로 선택했습니다. stackoverflow.com/questions/5669322/…
cqcn1991

4
이는 모든 API가 애플리케이션 간 트래픽을 제공함을 의미합니다 (단일 파트너에게 고유 한 키 및 비밀이 발급 됨). 이 시나리오에서는 서버 간 통신처럼이 대답이 적절합니다. 그러나 대부분의 웹 앱 개발자에게 혼란스러운 점은 사용자가 제어하고 작성한 Javascript 클라이언트의 경우 단일 키 비밀 (모든 클라이언트에 단일 키 비밀을 노출)을 사용하고 싶지 않다는 것입니다. 대신 Rail의 CSRF 및 쿠키 세션 메커니즘은 API를 사용하는 Javascript 앱에서도 훌륭하게 작동합니다. 각 요청과 함께 CSRF 토큰을 Rails에 다시 전달하면됩니다.
Jason FB

AJAX에서이 작업을 수행하는 방법은 this SO post stackoverflow.com/questions/7203304/…에
Jason FB

@JasonFB 당신은 자바 스크립트 클라이언트의 단일 비밀이 작동하지 않는다는 점에서 정확합니다. 그러나 다른 유형의 비 브라우저 클라이언트에서도 사용할 수있는 API를 빌드하려는 경우 세션 및 Rails CSRF 보호를 사용하는 것은 여전히 ​​문제가됩니다.
최대

CSRF에 대한 대안을 제공하거나 구축한다면 내 손님이 되십시오. 무엇을 교체하는 것이 적절한 지 알 수 있도록 교체하는 이유 만 알고 있어야합니다. 분명히, 이제 우리의 퀴블은 문맥이됩니다.
Jason FB 19

77

null 세션을 렌더링하지 않는 CSRF를 끄는 또 다른 방법은 다음을 추가하는 것입니다.

skip_before_action :verify_authenticity_token

Rails 컨트롤러에서. 이렇게하면 세션 정보에 계속 액세스 할 수 있습니다.

다시 말하지만 API 컨트롤러 또는 CSRF 보호가 적용되지 않는 다른 위치에서만이 작업을 수행해야합니다.


1
이것은 protect_from_forgery except: [:my_method_name]?
Arnold Roa

19

api.rubyonrails.org의 API 컨트롤러와 관련된 CSRF 구성에 대한 관련 정보가 있습니다 .

XML 또는 JSON 요청도 영향을받으며 API 를 빌드하는 경우 다음 에서 위조 방지 방법을 변경해야합니다 ApplicationController(기본값 :) :exception.

class ApplicationController < ActionController::Base
  protect_from_forgery unless: -> { request.format.json? }
end

일반적으로 상태 비 저장으로 설계되었으므로 API에 대한 CSRF 보호를 비활성화 할 수 있습니다. 즉, 요청 API 클라이언트가 Rails 대신 세션을 처리합니다.


4
너무 혼란 스러워요. protect_from_forgeryAPI에 여전히 필요하다고 말하는 출처와 그렇지 않다는 출처 사이에서 시사 하고 있습니다. API가 사용자 인증을 위해 세션 쿠키를 사용하는 단일 페이지 앱 용인 경우 어떻게됩니까?
Wylliam Judd

CSRF 메커니즘은 세션 쿠키를 사용하여 공격 벡터를 처리하는 Rails의 기본 제공 방식입니다. 이 메커니즘은 Rails 세션 쿠키와 함께 (실제로 내부에서) 작동하면서 컨트롤러를 보호합니다. protect_from_forgery를 사용하지 않거나이를 끄거나 except를 설정하지 않으면 Rails에게 CSRF 토큰 (세션 쿠키에서 가져온 정보)의 정보를 사용하여 해당 작업을 보호하지 않도록 지시하는 것입니다.
Jason FB

5
혼란스러운 점은 Rails 문서에서 "API"는 신뢰할 수있는 원격 파트너 (귀하의 사이트를 방문하는 모든 웹 사용자가 아님)로부터 API 요청을 수신하는 서버 간 애플리케이션을 의미한다는 것입니다. 이러한 ppl의 경우 해킹 불가능한 보안 메커니즘을 통해 고유 한 키-비밀 쌍을 제공하십시오. 많은 사람들이 웹 클라이언트를 통해 웹 앱을로드하는 최신 웹 애플리케이션의 경우 세션 또는 토큰 기반 메커니즘을 사용하여 사이트를 방문하는 각 사람을 고유하게 식별합니다. 따라서이를 수행하기 위해 SOME OTHER 메커니즘 (Json 웹 토큰 등)을 사용하지 않는 한 Rails의 내장 기능을 사용하십시오.
Jason FB

1
AJAX에서이 작업을 수행하는 방법은 this SO post stackoverflow.com/questions/7203304/…에
Jason FB

11

Rails 5부터 :: Base 대신 :: API 를 사용하여 새 클래스를 만들 수도 있습니다 .

class ApiController < ActionController::API
end

3

샘플 컨트롤러의 샘플 동작을 제외하려는 경우

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

외부의 요청을 문제없이 처리 할 수 ​​있습니다.


0

문제에 대한 가장 간단한 해결책은 컨트롤러에서 표준 작업을 수행하거나 직접 ApplicationController에 넣을 수 있습니다.

class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true end

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